diff options
Diffstat (limited to 'java/broker')
175 files changed, 10404 insertions, 4390 deletions
diff --git a/java/broker/bin/qpid-server b/java/broker/bin/qpid-server index 90b11da202..382004c9f5 100755 --- a/java/broker/bin/qpid-server +++ b/java/broker/bin/qpid-server @@ -33,8 +33,8 @@ if [ -z "$QPID_PNAME" ]; then export QPID_PNAME=" -DPNAME=QPBRKR" fi -# Set classpath to include Qpid jar with all required jars in manifest -QPID_LIBS=$QPID_HOME/lib/qpid-all.jar +# Set classpath to include the qpid-all manifest jar, and any jars supplied in lib/opt +QPID_LIBS="$QPID_HOME/lib/qpid-all.jar:$QPID_HOME/lib/opt/*" # Set other variables used by the qpid-run script before calling export JAVA=java \ @@ -51,6 +51,6 @@ QPID_OPTS="$QPID_OPTS -Damqj.read_write_pool_size=32 -DQPID_LOG_APPEND=$QPID_LOG if [ -z "$QPID_PID_FILENAME" ]; then export QPID_PID_FILENAME="qpid-server.pid" fi -echo $$ > ${QPID_WORK}/${QPID_PID_FILENAME} +echo $$ > "${QPID_WORK}/${QPID_PID_FILENAME}" -. ${QPID_HOME}/bin/qpid-run org.apache.qpid.server.Main "$@" +. "${QPID_HOME}/bin/qpid-run" org.apache.qpid.server.Main "$@" diff --git a/java/broker/bin/qpid-server.bat b/java/broker/bin/qpid-server.bat index c81f5fc3e7..af543decb3 100644 --- a/java/broker/bin/qpid-server.bat +++ b/java/broker/bin/qpid-server.bat @@ -108,13 +108,13 @@ goto beforeRunShift :runJdpa
REM USAGE: adds debugging options to the java command, use
-REM USAGE: JDPA_TRANSPORT and JPDA_ADDRESS to customize the debugging
+REM USAGE: JPDA_TRANSPORT and JPDA_ADDRESS to customize the debugging
REM USAGE: behavior and use JPDA_OPTS to override it entirely
-if "%JPDA_OPTS%" == "" goto beforeRunShift
-if "%JPDA_TRANSPORT%" == "" set JPDA_TRANSPORT=-dt_socket
+if not "%JPDA_OPTS%" == "" goto beforeRunShift
+if "%JPDA_TRANSPORT%" == "" set JPDA_TRANSPORT=dt_socket
if "%JPDA_ADDRESS%" == "" set JPDA_ADDRESS=8000
-set JPDA_OPTS="-Xdebug -Xrunjdwp:transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=n"
-set QPID_OPTS="%QPID_OPTS% %JPDA_OPTS%"
+set JPDA_OPTS=-Xdebug -Xrunjdwp:transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=n
+REM set QPID_OPTS="%QPID_OPTS% %JPDA_OPTS%"
goto beforeRunShift
:runExternalClasspath
@@ -192,7 +192,7 @@ rem QPID_OPTS intended to hold any -D props for use rem user must enclose any value for QPID_OPTS in double quotes
:runCommand
set MODULE_JARS=%QPID_MODULE_JARS%
-set COMMAND="%JAVA_HOME%\bin\java" %JAVA_VM% %JAVA_MEM% %JAVA_GC% %QPID_OPTS% %SYSTEM_PROPS% -cp "%CLASSPATH%;%MODULE_JARS%" org.apache.qpid.server.Main %QPID_ARGS%
+set COMMAND="%JAVA_HOME%\bin\java" %JAVA_VM% %JAVA_MEM% %JAVA_GC% %QPID_OPTS% %JPDA_OPTS% %SYSTEM_PROPS% -cp "%CLASSPATH%;%MODULE_JARS%" org.apache.qpid.server.Main %QPID_ARGS%
if "%debug%" == "true" echo %CLASSPATH%;%LAUNCH_JAR%;%MODULE_JARS%
if "%debug%" == "true" echo %COMMAND%
diff --git a/java/broker/build.xml b/java/broker/build.xml index edd71effaa..e733474ef0 100644 --- a/java/broker/build.xml +++ b/java/broker/build.xml @@ -76,6 +76,10 @@ <copy todir="${module.release}/lib/plugins" failonerror="true"> <fileset dir="${build.lib}/plugins"/> </copy> + <!--copy optional bdbstore module if it exists --> + <copy todir="${module.release}/lib/" failonerror="false"> + <fileset file="${build.lib}/${project.name}-bdbstore-${project.version}.jar"/> + </copy> </target> <target name="release-bin" depends="release-bin-tasks"/> diff --git a/java/broker/etc/access b/java/broker/etc/access deleted file mode 100644 index 58b7443fa9..0000000000 --- a/java/broker/etc/access +++ /dev/null @@ -1,19 +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.
-
-guest:localhost(rw),test(rw)
\ No newline at end of file diff --git a/java/broker/etc/config.xml b/java/broker/etc/config.xml index 14b9456067..d18e1392e6 100644 --- a/java/broker/etc/config.xml +++ b/java/broker/etc/config.xml @@ -30,28 +30,27 @@ <connector> <!-- To enable SSL edit the keystorePath and keystorePassword and set enabled to true. - To disasble Non-SSL port set sslOnly to true --> + To disable Non-SSL port set sslOnly to true --> <ssl> <enabled>false</enabled> + <port>5671</port> <sslOnly>false</sslOnly> - <keystorePath>/path/to/keystore.ks</keystorePath> - <keystorePassword>keystorepass</keystorePassword> + <keyStorePath>/path/to/keystore.ks</keyStorePath> + <keyStorePassword>keystorepass</keyStorePassword> </ssl> - <qpidnio>false</qpidnio> - <protectio> - <enabled>false</enabled> - <readBufferLimitSize>262144</readBufferLimitSize> - <writeBufferLimitSize>262144</writeBufferLimitSize> - </protectio> - <transport>nio</transport> <port>5672</port> - <sslport>8672</sslport> - <socketReceiveBuffer>32768</socketReceiveBuffer> - <socketSendBuffer>32768</socketSendBuffer> + <socketReceiveBuffer>262144</socketReceiveBuffer> + <socketSendBuffer>262144</socketSendBuffer> </connector> <management> <enabled>true</enabled> - <jmxport>8999</jmxport> + <jmxport> + <registryServer>8999</registryServer> + <!-- + If unspecified, connectorServer defaults to 100 + registryServer port. + <connectorServer>9099</connectionServer> + --> + </jmxport> <ssl> <enabled>false</enabled> <!-- Update below path to your keystore location, or run the bin/create-example-ssl-stores(.sh|.bat) @@ -69,10 +68,8 @@ </advanced> <security> - <principal-databases> - <!-- Example use of Base64 encoded MD5 hashes for authentication via CRAM-MD5-Hashed --> + <pd-auth-manager> <principal-database> - <name>passwordfile</name> <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> <attributes> <attribute> @@ -81,16 +78,11 @@ </attribute> </attributes> </principal-database> - </principal-databases> + </pd-auth-manager> <allow-all /> <msg-auth>false</msg-auth> - - <jmx> - <access>${conf}/jmxremote.access</access> - <principal-database>passwordfile</principal-database> - </jmx> </security> <virtualhosts>${conf}/virtualhosts.xml</virtualhosts> diff --git a/java/broker/etc/jmxremote.access b/java/broker/etc/jmxremote.access deleted file mode 100644 index 1a51a6991b..0000000000 --- a/java/broker/etc/jmxremote.access +++ /dev/null @@ -1,23 +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.
-
-#Generated by JMX Console : Last edited by user:admin
-#Tue Jun 12 16:46:39 BST 2007
-admin=admin
-guest=readonly
-user=readwrite
diff --git a/java/broker/etc/passwdVhost b/java/broker/etc/passwdVhost deleted file mode 100644 index 48ce8299b6..0000000000 --- a/java/broker/etc/passwdVhost +++ /dev/null @@ -1,19 +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. -# -guest:guest:localhost,test diff --git a/java/broker/etc/qpid-server.conf.jpp b/java/broker/etc/qpid-server.conf.jpp index 3ed2431ef3..0378c82fd9 100644 --- a/java/broker/etc/qpid-server.conf.jpp +++ b/java/broker/etc/qpid-server.conf.jpp @@ -17,8 +17,7 @@ # under the License. # -QPID_LIBS=$(build-classpath backport-util-concurrent \ - commons-beanutils \ +QPID_LIBS=$(build-classpath commons-beanutils \ commons-beanutils-core \ commons-cli \ commons-codec \ diff --git a/java/broker/etc/virtualhosts.xml b/java/broker/etc/virtualhosts.xml index 5860bfe2cb..33a48a1349 100644 --- a/java/broker/etc/virtualhosts.xml +++ b/java/broker/etc/virtualhosts.xml @@ -31,7 +31,7 @@ <housekeeping> <threadCount>2</threadCount> - <expiredMessageCheckPeriod>20000</expiredMessageCheckPeriod> + <checkPeriod>20000</checkPeriod> </housekeeping> <exchanges> diff --git a/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java b/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java index c0afae0773..6abef6fd6b 100644 --- a/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java +++ b/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java @@ -694,7 +694,8 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable public BrokerSchema.BrokerClass.QueueMoveMessagesMethodResponseCommand queueMoveMessages(final BrokerSchema.BrokerClass.QueueMoveMessagesMethodResponseCommandFactory factory, final String srcQueue, final String destQueue, - final Long qty) + final Long qty, + final Map filter) // TODO: move based on group identifier { // TODO return factory.createResponseCommand(CompletionCode.NOT_IMPLEMENTED); @@ -712,6 +713,46 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable return factory.createResponseCommand(CompletionCode.NOT_IMPLEMENTED); } + public BrokerSchema.BrokerClass.GetTimestampConfigMethodResponseCommand getTimestampConfig(final BrokerSchema.BrokerClass.GetTimestampConfigMethodResponseCommandFactory factory) + { + // TODO: timestamp support + return factory.createResponseCommand(CompletionCode.NOT_IMPLEMENTED); + } + + public BrokerSchema.BrokerClass.SetTimestampConfigMethodResponseCommand setTimestampConfig(final BrokerSchema.BrokerClass.SetTimestampConfigMethodResponseCommandFactory factory, + final java.lang.Boolean receive) + { + // TODO: timestamp support + return factory.createResponseCommand(CompletionCode.NOT_IMPLEMENTED); + } + + public BrokerSchema.BrokerClass.CreateMethodResponseCommand create(final BrokerSchema.BrokerClass.CreateMethodResponseCommandFactory factory, + final String type, + final String name, + final Map properties, + final java.lang.Boolean lenient) + { + //TODO: + return factory.createResponseCommand(CompletionCode.NOT_IMPLEMENTED); + } + + public BrokerSchema.BrokerClass.DeleteMethodResponseCommand delete(final BrokerSchema.BrokerClass.DeleteMethodResponseCommandFactory factory, + final String type, + final String name, + final Map options) + { + //TODO: + return factory.createResponseCommand(CompletionCode.NOT_IMPLEMENTED); + } + + public BrokerSchema.BrokerClass.QueryMethodResponseCommand query(final BrokerSchema.BrokerClass.QueryMethodResponseCommandFactory factory, + final String type, + final String name) + { + //TODO: + return factory.createResponseCommand(CompletionCode.NOT_IMPLEMENTED); + } + public UUID getId() { return _obj.getId(); @@ -1072,8 +1113,19 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable return 0l; } + public Boolean getFlowStopped() + { + return Boolean.FALSE; + } + + public Long getFlowStoppedCount() + { + return 0L; + } + public BrokerSchema.QueueClass.PurgeMethodResponseCommand purge(final BrokerSchema.QueueClass.PurgeMethodResponseCommandFactory factory, - final Long request) + final Long request, + final Map filter) // TODO: support for purge-by-group-identifier { try { @@ -1089,7 +1141,8 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable public BrokerSchema.QueueClass.RerouteMethodResponseCommand reroute(final BrokerSchema.QueueClass.RerouteMethodResponseCommandFactory factory, final Long request, final Boolean useAltExchange, - final String exchange) + final String exchange, + final Map filter) // TODO: support for re-route-by-group-identifier { //TODO return factory.createResponseCommand(CompletionCode.NOT_IMPLEMENTED); @@ -1282,6 +1335,23 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable { return _obj.isShadow(); } + + public Boolean getUserProxyAuth() + { + // TODO + return false; + } + + public String getSaslMechanism() + { + // TODO + return null; + } + public Integer getSaslSsf() + { + // TODO + return 0; + } } private class SessionDelegate implements BrokerSchema.SessionDelegate diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index a612f280d6..d1ea5dba69 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -327,4 +327,74 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr { return getObjectNameForSingleInstanceMBean(); } + + public void resetStatistics() throws Exception + { + getVirtualHost().resetStatistics(); + } + + public double getPeakMessageDeliveryRate() + { + return getVirtualHost().getMessageDeliveryStatistics().getPeak(); + } + + public double getPeakDataDeliveryRate() + { + return getVirtualHost().getDataDeliveryStatistics().getPeak(); + } + + public double getMessageDeliveryRate() + { + return getVirtualHost().getMessageDeliveryStatistics().getRate(); + } + + public double getDataDeliveryRate() + { + return getVirtualHost().getDataDeliveryStatistics().getRate(); + } + + public long getTotalMessagesDelivered() + { + return getVirtualHost().getMessageDeliveryStatistics().getTotal(); + } + + public long getTotalDataDelivered() + { + return getVirtualHost().getDataDeliveryStatistics().getTotal(); + } + + public double getPeakMessageReceiptRate() + { + return getVirtualHost().getMessageReceiptStatistics().getPeak(); + } + + public double getPeakDataReceiptRate() + { + return getVirtualHost().getDataReceiptStatistics().getPeak(); + } + + public double getMessageReceiptRate() + { + return getVirtualHost().getMessageReceiptStatistics().getRate(); + } + + public double getDataReceiptRate() + { + return getVirtualHost().getDataReceiptStatistics().getRate(); + } + + public long getTotalMessagesReceived() + { + return getVirtualHost().getMessageReceiptStatistics().getTotal(); + } + + public long getTotalDataReceived() + { + return getVirtualHost().getDataReceiptStatistics().getTotal(); + } + + public boolean isStatisticsEnabled() + { + return getVirtualHost().isStatisticsEnabled(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index 4f86c82578..34bc57a826 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -22,6 +22,7 @@ package org.apache.qpid.server; import org.apache.log4j.Logger; +import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQMethodBody; @@ -49,6 +50,7 @@ import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.actors.AMQPChannelActor; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.ChannelMessages; +import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.logging.subjects.ChannelLogSubject; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.MessageMetaData; @@ -74,6 +76,7 @@ import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.TransportException; import java.util.ArrayList; import java.util.Collection; @@ -136,11 +139,12 @@ public class AMQChannel implements SessionConfig, AMQSessionModel private final AtomicBoolean _suspended = new AtomicBoolean(false); private ServerTransaction _transaction; - + private final AtomicLong _txnStarts = new AtomicLong(0); private final AtomicLong _txnCommits = new AtomicLong(0); private final AtomicLong _txnRejects = new AtomicLong(0); private final AtomicLong _txnCount = new AtomicLong(0); + private final AtomicLong _txnUpdateTime = new AtomicLong(0); private final AMQProtocolSession _session; private AtomicBoolean _closing = new AtomicBoolean(false); @@ -199,7 +203,12 @@ public class AMQChannel implements SessionConfig, AMQSessionModel // theory return !(_transaction instanceof AutoCommitTransaction); } - + + public boolean inTransaction() + { + return isTransactional() && _txnUpdateTime.get() > 0 && _transaction.getTransactionStartTime() > 0; + } + private void incrementOutstandingTxnsIfNecessary() { if(isTransactional()) @@ -209,7 +218,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel _txnCount.compareAndSet(0,1); } } - + private void decrementOutstandingTxnsIfNecessary() { if(isTransactional()) @@ -295,7 +304,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel }); deliverCurrentMessageIfComplete(); - } } @@ -308,7 +316,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel try { _currentMessage.getStoredMessage().flushToStore(); - final ArrayList<? extends BaseQueue> destinationQueues = _currentMessage.getDestinationQueues(); if(!checkMessageUserId(_currentMessage.getContentHeader())) @@ -317,7 +324,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } else { - if(destinationQueues == null || _currentMessage.getDestinationQueues().isEmpty()) + if(destinationQueues == null || destinationQueues.isEmpty()) { if (_currentMessage.isMandatory() || _currentMessage.isImmediate()) { @@ -325,7 +332,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } else { - _logger.warn("MESSAGE DISCARDED: No routes for message - " + createAMQMessage(_currentMessage)); + _actor.message(ExchangeMessages.DISCARDMSG(_currentMessage.getExchange().asString(), _currentMessage.getRoutingKey())); } } @@ -333,11 +340,15 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { _transaction.enqueue(destinationQueues, _currentMessage, new MessageDeliveryAction(_currentMessage, destinationQueues, isTransactional())); incrementOutstandingTxnsIfNecessary(); + updateTransactionalActivity(); } } } finally { + long bodySize = _currentMessage.getSize(); + long timestamp = ((BasicContentHeaderProperties) _currentMessage.getContentHeader().getProperties()).getTimestamp(); + _session.registerMessageReceived(bodySize, timestamp); _currentMessage = null; } } @@ -375,6 +386,13 @@ public class AMQChannel implements SessionConfig, AMQSessionModel _currentMessage = null; throw e; } + catch (RuntimeException e) + { + // we want to make sure we don't keep a reference to the message in the + // event of an error + _currentMessage = null; + throw e; + } } protected void routeCurrentMessage() throws AMQException @@ -425,7 +443,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { throw new AMQException("Consumer already exists with same tag: " + tag); } - + Subscription subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(_channelId, _session, tag, acks, filters, noLocal, _creditManager); @@ -446,6 +464,11 @@ public class AMQChannel implements SessionConfig, AMQSessionModel _tag2SubscriptionMap.remove(tag); throw e; } + catch (RuntimeException e) + { + _tag2SubscriptionMap.remove(tag); + throw e; + } return tag; } @@ -503,7 +526,11 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } catch (AMQException e) { - _logger.error("Caught AMQException whilst attempting to reque:" + e); + _logger.error("Caught AMQException whilst attempting to requeue:" + e); + } + catch (TransportException e) + { + _logger.error("Caught TransportException whilst attempting to requeue:" + e); } getConfigStore().removeConfiguredObject(this); @@ -794,6 +821,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { Collection<QueueEntry> ackedMessages = getAckedMessages(deliveryTag, multiple); _transaction.dequeue(ackedMessages, new MessageAcknowledgeAction(ackedMessages)); + updateTransactionalActivity(); } private Collection<QueueEntry> getAckedMessages(long deliveryTag, boolean multiple) @@ -933,7 +961,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel finally { _rollingBack = false; - + _txnRejects.incrementAndGet(); _txnStarts.incrementAndGet(); decrementOutstandingTxnsIfNecessary(); @@ -968,6 +996,17 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } + /** + * Update last transaction activity timestamp + */ + private void updateTransactionalActivity() + { + if (isTransactional()) + { + _txnUpdateTime.set(System.currentTimeMillis()); + } + } + public String toString() { return "["+_session.toString()+":"+_channelId+"]"; @@ -1016,6 +1055,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) throws AMQException { + _session.registerMessageDelivered(entry.getMessage().getSize()); getProtocolSession().getProtocolOutputConverter().writeDeliver(entry, getChannelId(), deliveryTag, sub.getConsumerTag()); } @@ -1056,11 +1096,11 @@ public class AMQChannel implements SessionConfig, AMQSessionModel private boolean checkMessageUserId(ContentHeaderBody header) { AMQShortString userID = - header.properties instanceof BasicContentHeaderProperties - ? ((BasicContentHeaderProperties) header.properties).getUserId() + header.getProperties() instanceof BasicContentHeaderProperties + ? ((BasicContentHeaderProperties) header.getProperties()).getUserId() : null; - return (!MSG_AUTH || _session.getPrincipal().getName().equals(userID == null? "" : userID.toString())); + return (!MSG_AUTH || _session.getAuthorizedPrincipal().getName().equals(userID == null? "" : userID.toString())); } @@ -1402,9 +1442,41 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { return _createTime; } - + public void mgmtClose() throws AMQException { _session.mgmtCloseChannel(_channelId); } + + public void checkTransactionStatus(long openWarn, long openClose, long idleWarn, long idleClose) throws AMQException + { + if (inTransaction()) + { + long currentTime = System.currentTimeMillis(); + long openTime = currentTime - _transaction.getTransactionStartTime(); + long idleTime = currentTime - _txnUpdateTime.get(); + + // Log a warning on idle or open transactions + if (idleWarn > 0L && idleTime > idleWarn) + { + CurrentActor.get().message(_logSubject, ChannelMessages.IDLE_TXN(idleTime)); + _logger.warn("IDLE TRANSACTION ALERT " + _logSubject.toString() + " " + idleTime + " ms"); + } + else if (openWarn > 0L && openTime > openWarn) + { + CurrentActor.get().message(_logSubject, ChannelMessages.OPEN_TXN(openTime)); + _logger.warn("OPEN TRANSACTION ALERT " + _logSubject.toString() + " " + openTime + " ms"); + } + + // Close connection for idle or open transactions that have timed out + if (idleClose > 0L && idleTime > idleClose) + { + getConnectionModel().closeSession(this, AMQConstant.RESOURCE_ERROR, "Idle transaction timed out"); + } + else if (openClose > 0L && openTime > openClose) + { + getConnectionModel().closeSession(this, AMQConstant.RESOURCE_ERROR, "Open transaction timed out"); + } + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/Broker.java b/java/broker/src/main/java/org/apache/qpid/server/Broker.java new file mode 100644 index 0000000000..1632effaf0 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/Broker.java @@ -0,0 +1,441 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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; + +import static org.apache.qpid.transport.ConnectionSettings.WILDCARD_ADDRESS; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import javax.net.ssl.SSLContext; + +import org.apache.log4j.PropertyConfigurator; +import org.apache.log4j.xml.QpidLog4JConfigurator; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.configuration.ServerNetworkTransportConfiguration; +import org.apache.qpid.server.configuration.management.ConfigurationManagementMBean; +import org.apache.qpid.server.information.management.ServerInformationMBean; +import org.apache.qpid.server.logging.SystemOutMessageLogger; +import org.apache.qpid.server.logging.actors.BrokerActor; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.GenericActor; +import org.apache.qpid.server.logging.management.LoggingManagementMBean; +import org.apache.qpid.server.logging.messages.BrokerMessages; +import org.apache.qpid.server.protocol.AmqpProtocolVersion; +import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; +import org.apache.qpid.server.transport.QpidAcceptor; +import org.apache.qpid.ssl.SSLContextFactory; +import org.apache.qpid.transport.NetworkTransportConfiguration; +import org.apache.qpid.transport.network.IncomingNetworkTransport; +import org.apache.qpid.transport.network.Transport; + +public class Broker +{ + private static final int IPV4_ADDRESS_LENGTH = 4; + private static final char IPV4_LITERAL_SEPARATOR = '.'; + + protected static class InitException extends RuntimeException + { + private static final long serialVersionUID = 1L; + + InitException(String msg, Throwable cause) + { + super(msg, cause); + } + } + + public void shutdown() + { + ApplicationRegistry.remove(); + } + + public void startup() throws Exception + { + startup(new BrokerOptions()); + } + + public void startup(final BrokerOptions options) throws Exception + { + try + { + CurrentActor.set(new BrokerActor(new SystemOutMessageLogger())); + startupImpl(options); + } + finally + { + CurrentActor.remove(); + } + } + + private void startupImpl(final BrokerOptions options) throws Exception + { + final String qpidHome = options.getQpidHome(); + final File configFile = getConfigFile(options.getConfigFile(), + BrokerOptions.DEFAULT_CONFIG_FILE, qpidHome, true); + + CurrentActor.get().message(BrokerMessages.CONFIG(configFile.getAbsolutePath())); + + File logConfigFile = getConfigFile(options.getLogConfigFile(), + BrokerOptions.DEFAULT_LOG_CONFIG_FILE, qpidHome, false); + + configureLogging(logConfigFile, options.getLogWatchFrequency()); + + ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(configFile, options.getBundleContext()); + ServerConfiguration serverConfig = config.getConfiguration(); + updateManagementPorts(serverConfig, options.getJmxPortRegistryServer(), options.getJmxPortConnectorServer()); + + ApplicationRegistry.initialise(config); + + // We have already loaded the BrokerMessages class by this point so we + // need to refresh the locale setting incase we had a different value in + // the configuration. + BrokerMessages.reload(); + + // AR.initialise() sets and removes its own actor so we now need to set the actor + // for the remainder of the startup, and the default actor if the stack is empty + CurrentActor.set(new BrokerActor(config.getCompositeStartupMessageLogger())); + CurrentActor.setDefault(new BrokerActor(config.getRootMessageLogger())); + GenericActor.setDefaultMessageLogger(config.getRootMessageLogger()); + + try + { + configureLoggingManagementMBean(logConfigFile, options.getLogWatchFrequency()); + + ConfigurationManagementMBean configMBean = new ConfigurationManagementMBean(); + configMBean.register(); + + ServerInformationMBean sysInfoMBean = new ServerInformationMBean(config); + sysInfoMBean.register(); + + Set<Integer> ports = new HashSet<Integer>(options.getPorts()); + if(ports.isEmpty()) + { + parsePortList(ports, serverConfig.getPorts()); + } + + Set<Integer> sslPorts = new HashSet<Integer>(options.getSSLPorts()); + if(sslPorts.isEmpty()) + { + parsePortList(sslPorts, serverConfig.getSSLPorts()); + } + + Set<Integer> exclude_0_10 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v0_10)); + if(exclude_0_10.isEmpty()) + { + parsePortList(exclude_0_10, serverConfig.getPortExclude010()); + } + + Set<Integer> exclude_0_9_1 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v0_9_1)); + if(exclude_0_9_1.isEmpty()) + { + parsePortList(exclude_0_9_1, serverConfig.getPortExclude091()); + } + + Set<Integer> exclude_0_9 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v0_9)); + if(exclude_0_9.isEmpty()) + { + parsePortList(exclude_0_9, serverConfig.getPortExclude09()); + } + + Set<Integer> exclude_0_8 = new HashSet<Integer>(options.getExcludedPorts(ProtocolExclusion.v0_8)); + if(exclude_0_8.isEmpty()) + { + parsePortList(exclude_0_8, serverConfig.getPortExclude08()); + } + + String bindAddr = options.getBind(); + if (bindAddr == null) + { + bindAddr = serverConfig.getBind(); + } + + InetAddress bindAddress = null; + if (bindAddr.equals(WILDCARD_ADDRESS)) + { + bindAddress = new InetSocketAddress(0).getAddress(); + } + else + { + bindAddress = InetAddress.getByAddress(parseIP(bindAddr)); + } + String hostName = bindAddress.getCanonicalHostName(); + + if (!serverConfig.getSSLOnly()) + { + for(int port : ports) + { + final Set<AmqpProtocolVersion> supported = + getSupportedVersions(port, exclude_0_10, exclude_0_9_1, exclude_0_9, exclude_0_8); + final NetworkTransportConfiguration settings = + new ServerNetworkTransportConfiguration(serverConfig, port, bindAddress.getHostName(), Transport.TCP); + + final IncomingNetworkTransport transport = Transport.getIncomingTransportInstance(); + final MultiVersionProtocolEngineFactory protocolEngineFactory = + new MultiVersionProtocolEngineFactory(hostName, supported); + + transport.accept(settings, protocolEngineFactory, null); + ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, port), + new QpidAcceptor(transport,"TCP")); + CurrentActor.get().message(BrokerMessages.LISTENING("TCP", port)); + } + } + + if (serverConfig.getEnableSSL()) + { + final String keystorePath = serverConfig.getConnectorKeyStorePath(); + final String keystorePassword = serverConfig.getConnectorKeyStorePassword(); + final String certType = serverConfig.getConnectorCertType(); + final SSLContext sslContext = SSLContextFactory.buildServerContext(keystorePath, keystorePassword, certType); + + for(int sslPort : sslPorts) + { + final Set<AmqpProtocolVersion> supported = + getSupportedVersions(sslPort, exclude_0_10, exclude_0_9_1, exclude_0_9, exclude_0_8); + final NetworkTransportConfiguration settings = + new ServerNetworkTransportConfiguration(serverConfig, sslPort, bindAddress.getHostName(), Transport.TCP); + + final IncomingNetworkTransport transport = Transport.getIncomingTransportInstance(); + final MultiVersionProtocolEngineFactory protocolEngineFactory = + new MultiVersionProtocolEngineFactory(hostName, supported); + + transport.accept(settings, protocolEngineFactory, sslContext); + ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, sslPort), + new QpidAcceptor(transport,"TCP")); + CurrentActor.get().message(BrokerMessages.LISTENING("TCP/SSL", sslPort)); + } + } + + CurrentActor.get().message(BrokerMessages.READY()); + } + finally + { + // Startup is complete so remove the AR initialised Startup actor + CurrentActor.remove(); + } + } + + private static Set<AmqpProtocolVersion> getSupportedVersions(final int port, final Set<Integer> exclude_0_10, + final Set<Integer> exclude_0_9_1, final Set<Integer> exclude_0_9, + final Set<Integer> exclude_0_8) + { + final EnumSet<AmqpProtocolVersion> supported = EnumSet.allOf(AmqpProtocolVersion.class); + + if(exclude_0_10.contains(port)) + { + supported.remove(AmqpProtocolVersion.v0_10); + } + if(exclude_0_9_1.contains(port)) + { + supported.remove(AmqpProtocolVersion.v0_9_1); + } + if(exclude_0_9.contains(port)) + { + supported.remove(AmqpProtocolVersion.v0_9); + } + if(exclude_0_8.contains(port)) + { + supported.remove(AmqpProtocolVersion.v0_8); + } + + return supported; + } + + private File getConfigFile(final String fileName, + final String defaultFileName, + final String qpidHome, boolean throwOnFileNotFound) throws InitException + { + File configFile = null; + if (fileName != null) + { + configFile = new File(fileName); + } + else + { + configFile = new File(qpidHome, defaultFileName); + } + + if (!configFile.exists() && throwOnFileNotFound) + { + String error = "File " + fileName + " could not be found. Check the file exists and is readable."; + + if (qpidHome == null) + { + error = error + "\nNote: " + BrokerOptions.QPID_HOME + " is not set."; + } + + throw new InitException(error, null); + } + + return configFile; + } + + public static void parsePortList(Set<Integer> output, List<?> ports) throws InitException + { + if(ports != null) + { + for(Object o : ports) + { + try + { + output.add(Integer.parseInt(String.valueOf(o))); + } + catch (NumberFormatException e) + { + throw new InitException("Invalid port: " + o, e); + } + } + } + } + + /** + * Update the configuration data with the management port. + * @param configuration + * @param registryServerPort The string from the command line + */ + private void updateManagementPorts(ServerConfiguration configuration, Integer registryServerPort, Integer connectorServerPort) + { + if (registryServerPort != null) + { + try + { + configuration.setJMXPortRegistryServer(registryServerPort); + } + catch (NumberFormatException e) + { + throw new InitException("Invalid management (registry server) port: " + registryServerPort, null); + } + } + if (connectorServerPort != null) + { + try + { + configuration.setJMXPortConnectorServer(connectorServerPort); + } + catch (NumberFormatException e) + { + throw new InitException("Invalid management (connector server) port: " + connectorServerPort, null); + } + } + } + + private byte[] parseIP(String address) throws Exception + { + char[] literalBuffer = address.toCharArray(); + int byteCount = 0; + int currByte = 0; + byte[] ip = new byte[IPV4_ADDRESS_LENGTH]; + for (int i = 0; i < literalBuffer.length; i++) + { + char currChar = literalBuffer[i]; + if ((currChar >= '0') && (currChar <= '9')) + { + currByte = (currByte * 10) + (Character.digit(currChar, 10) & 0xFF); + } + + if (currChar == IPV4_LITERAL_SEPARATOR || (i + 1 == literalBuffer.length)) + { + ip[byteCount++] = (byte) currByte; + currByte = 0; + } + } + + if (byteCount != 4) + { + throw new Exception("Invalid IP address: " + address); + } + return ip; + } + + private void configureLogging(File logConfigFile, long logWatchTime) throws InitException, IOException + { + if (logConfigFile.exists() && logConfigFile.canRead()) + { + CurrentActor.get().message(BrokerMessages.LOG_CONFIG(logConfigFile.getAbsolutePath())); + + if (logWatchTime > 0) + { + System.out.println("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every " + + logWatchTime + " seconds"); + // log4j expects the watch interval in milliseconds + try + { + QpidLog4JConfigurator.configureAndWatch(logConfigFile.getPath(), logWatchTime * 1000); + } + catch (Exception e) + { + throw new InitException(e.getMessage(),e); + } + } + else + { + try + { + QpidLog4JConfigurator.configure(logConfigFile.getPath()); + } + catch (Exception e) + { + throw new InitException(e.getMessage(),e); + } + } + } + else + { + System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath()); + System.err.println("Using the fallback internal log4j.properties configuration"); + + InputStream propsFile = this.getClass().getResourceAsStream("/log4j.properties"); + if(propsFile == null) + { + throw new IOException("Unable to load the fallback internal log4j.properties configuration file"); + } + else + { + try + { + Properties fallbackProps = new Properties(); + fallbackProps.load(propsFile); + PropertyConfigurator.configure(fallbackProps); + } + finally + { + propsFile.close(); + } + } + } + } + + private void configureLoggingManagementMBean(File logConfigFile, int logWatchTime) throws Exception + { + LoggingManagementMBean blm = new LoggingManagementMBean(logConfigFile.getPath(),logWatchTime); + + blm.register(); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java b/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java new file mode 100644 index 0000000000..3defd8260c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.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.server; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.osgi.framework.BundleContext; + +public class BrokerOptions +{ + /** serialVersionUID */ + private static final long serialVersionUID = 8051825964945442234L; + + public static final String DEFAULT_CONFIG_FILE = "etc/config.xml"; + public static final String DEFAULT_LOG_CONFIG_FILE = "etc/log4j.xml"; + public static final String QPID_HOME = "QPID_HOME"; + + private final Set<Integer> _ports = new HashSet<Integer>(); + private final Set<Integer> _sslPorts = new HashSet<Integer>(); + private final Map<ProtocolExclusion,Set<Integer>> _exclusionMap = new HashMap<ProtocolExclusion, Set<Integer>>(); + + private String _configFile; + private String _logConfigFile; + private String _bind; + private Integer _jmxPortRegistryServer; + private Integer _jmxPortConnectorServer; + private BundleContext _bundleContext; + + private Integer _logWatchFrequency = 0; + + + public void addPort(final int port) + { + _ports.add(port); + } + + public void addSSLPort(final int sslPort) + { + _sslPorts.add(sslPort); + } + + public Set<Integer> getPorts() + { + return Collections.unmodifiableSet(_ports); + } + + public Set<Integer> getSSLPorts() + { + return Collections.unmodifiableSet(_sslPorts); + } + + public String getConfigFile() + { + return _configFile; + } + + public void setConfigFile(final String configFile) + { + _configFile = configFile; + } + + public String getLogConfigFile() + { + return _logConfigFile; + } + + public void setLogConfigFile(final String logConfigFile) + { + _logConfigFile = logConfigFile; + } + + public Integer getJmxPortRegistryServer() + { + return _jmxPortRegistryServer; + } + + public void setJmxPortRegistryServer(final int jmxPortRegistryServer) + { + _jmxPortRegistryServer = jmxPortRegistryServer; + } + + public Integer getJmxPortConnectorServer() + { + return _jmxPortConnectorServer; + } + + public void setJmxPortConnectorServer(final int jmxPortConnectorServer) + { + _jmxPortConnectorServer = jmxPortConnectorServer; + } + + public String getQpidHome() + { + return System.getProperty(QPID_HOME); + } + + public Set<Integer> getExcludedPorts(final ProtocolExclusion excludeProtocol) + { + final Set<Integer> excludedPorts = _exclusionMap.get(excludeProtocol); + return excludedPorts == null ? Collections.<Integer>emptySet() : excludedPorts; + } + + public void addExcludedPort(final ProtocolExclusion excludeProtocol, final int port) + { + if (!_exclusionMap.containsKey(excludeProtocol)) + { + _exclusionMap.put(excludeProtocol, new HashSet<Integer>()); + } + + Set<Integer> ports = _exclusionMap.get(excludeProtocol); + ports.add(port); + } + + public String getBind() + { + return _bind; + } + + public void setBind(final String bind) + { + _bind = bind; + } + + public int getLogWatchFrequency() + { + return _logWatchFrequency; + } + + /** + * Set the frequency with which the log config file will be checked for updates. + * @param logWatchFrequency frequency in seconds + */ + public void setLogWatchFrequency(final int logWatchFrequency) + { + _logWatchFrequency = logWatchFrequency; + } + + public BundleContext getBundleContext() + { + return _bundleContext ; + } + + public void setBundleContext(final BundleContext bundleContext) + { + _bundleContext = bundleContext; + } + +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/Main.java b/java/broker/src/main/java/org/apache/qpid/server/Main.java index 71cf17ed60..0c038c7800 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -20,17 +20,6 @@ */ 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.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Properties; -import java.util.Set; - import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; @@ -38,29 +27,9 @@ import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; 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.qpid.common.QpidProperties; -import org.apache.qpid.framing.ProtocolVersion; -import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.configuration.management.ConfigurationManagementMBean; -import org.apache.qpid.server.information.management.ServerInformationMBean; -import org.apache.qpid.server.logging.SystemOutMessageLogger; -import org.apache.qpid.server.logging.actors.BrokerActor; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.GenericActor; -import org.apache.qpid.server.logging.management.LoggingManagementMBean; -import org.apache.qpid.server.logging.messages.BrokerMessages; -import org.apache.qpid.server.protocol.AMQProtocolEngineFactory; -import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory; -import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory.VERSION; +import org.apache.qpid.server.Broker.InitException; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; -import org.apache.qpid.server.transport.QpidAcceptor; -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. @@ -68,41 +37,129 @@ import org.apache.qpid.transport.network.mina.MINANetworkDriver; */ public class Main { - private static Logger _logger; - private static final String DEFAULT_CONFIG_FILE = "etc/config.xml"; - - public static final String DEFAULT_LOG_CONFIG_FILENAME = "log4j.xml"; - public static final String QPID_HOME = "QPID_HOME"; - private static final int IPV4_ADDRESS_LENGTH = 4; + private static final Option OPTION_HELP = new Option("h", "help", false, "print this message"); + + private static final Option OPTION_VERSION = new Option("v", "version", false, "print the version information and exit"); + + private static final Option OPTION_CONFIG_FILE = + OptionBuilder.withArgName("file").hasArg().withDescription("use given configuration file").withLongOpt("config") + .create("c"); + + private static final Option OPTION_PORT = + OptionBuilder.withArgName("port").hasArg() + .withDescription("listen on the specified port. Overrides any value in the config file") + .withLongOpt("port").create("p"); + + private static final Option OPTION_SSLPORT = + OptionBuilder.withArgName("port").hasArg() + .withDescription("SSL port. Overrides any value in the config file") + .withLongOpt("sslport").create("s"); + + private static final Option OPTION_EXCLUDE_0_10 = + OptionBuilder.withArgName("port").hasArg() + .withDescription("when listening on the specified port do not accept AMQP0-10 connections. The specified port must be one specified on the command line") + .withLongOpt("exclude-0-10").create(); + + private static final Option OPTION_EXCLUDE_0_9_1 = + OptionBuilder.withArgName("port").hasArg() + .withDescription("when listening on the specified port do not accept AMQP0-9-1 connections. The specified port must be one specified on the command line") + .withLongOpt("exclude-0-9-1").create(); + + private static final Option OPTION_EXCLUDE_0_9 = + OptionBuilder.withArgName("port").hasArg() + .withDescription("when listening on the specified port do not accept AMQP0-9 connections. The specified port must be one specified on the command line") + .withLongOpt("exclude-0-9").create(); + + private static final Option OPTION_EXCLUDE_0_8 = + OptionBuilder.withArgName("port").hasArg() + .withDescription("when listening on the specified port do not accept AMQP0-8 connections. The specified port must be one specified on the command line") + .withLongOpt("exclude-0-8").create(); + + private static final Option OPTION_JMX_PORT_REGISTRY_SERVER = + OptionBuilder.withArgName("port").hasArg() + .withDescription("listen on the specified management (registry server) port. Overrides any value in the config file") + .withLongOpt("jmxregistryport").create("m"); + + private static final Option OPTION_JMX_PORT_CONNECTOR_SERVER = + OptionBuilder.withArgName("port").hasArg() + .withDescription("listen on the specified management (connector server) port. Overrides any value in the config file") + .withLongOpt("jmxconnectorport").create(); + + private static final Option OPTION_BIND = + OptionBuilder.withArgName("address").hasArg() + .withDescription("bind to the specified address. Overrides any value in the config file") + .withLongOpt("bind").create("b"); + + private static final Option OPTION_LOG_CONFIG_FILE = + OptionBuilder.withArgName("file").hasArg() + .withDescription("use the specified log4j xml configuration file. By " + + "default looks for a file named " + BrokerOptions.DEFAULT_LOG_CONFIG_FILE + + " in the same directory as the configuration file").withLongOpt("logconfig").create("l"); + + private static final Option OPTION_LOG_WATCH = + OptionBuilder.withArgName("period").hasArg() + .withDescription("monitor the log file configuration file for changes. Units are seconds. " + + "Zero means do not check for changes.").withLongOpt("logwatch").create("w"); + + private static final Options OPTIONS = new Options(); + + static + { + OPTIONS.addOption(OPTION_HELP); + OPTIONS.addOption(OPTION_VERSION); + OPTIONS.addOption(OPTION_CONFIG_FILE); + OPTIONS.addOption(OPTION_LOG_CONFIG_FILE); + OPTIONS.addOption(OPTION_LOG_WATCH); + OPTIONS.addOption(OPTION_PORT); + OPTIONS.addOption(OPTION_SSLPORT); + OPTIONS.addOption(OPTION_EXCLUDE_0_10); + OPTIONS.addOption(OPTION_EXCLUDE_0_9_1); + OPTIONS.addOption(OPTION_EXCLUDE_0_9); + OPTIONS.addOption(OPTION_EXCLUDE_0_8); + OPTIONS.addOption(OPTION_BIND); + + OPTIONS.addOption(OPTION_JMX_PORT_REGISTRY_SERVER); + OPTIONS.addOption(OPTION_JMX_PORT_CONNECTOR_SERVER); + } - private static final char IPV4_LITERAL_SEPARATOR = '.'; + private CommandLine commandLine; - protected static class InitException extends Exception + public static void main(String[] args) { - InitException(String msg, Throwable cause) + //if the -Dlog4j.configuration property has not been set, enable the init override + //to stop Log4J wondering off and picking up the first log4j.xml/properties file it + //finds from the classpath when we get the first Loggers + if(System.getProperty("log4j.configuration") == null) { - super(msg, cause); + System.setProperty("log4j.defaultInitOverride", "true"); } - } - protected final Options options = new Options(); - protected CommandLine commandLine; + new Main(args); + } - protected Main(String[] args) + public Main(final String[] args) { - setOptions(options); if (parseCommandline(args)) { - execute(); + try + { + execute(); + } + catch(Throwable e) + { + System.err.println("Exception during startup: " + e); + e.printStackTrace(); + shutdown(1); + } } } - protected boolean parseCommandline(String[] args) + protected boolean parseCommandline(final String[] args) { try { - commandLine = new PosixParser().parse(options, args); + commandLine = new PosixParser().parse(OPTIONS, args); return true; } @@ -110,509 +167,129 @@ public class Main { System.err.println("Error: " + e.getMessage()); HelpFormatter formatter = new HelpFormatter(); - formatter.printHelp("Qpid", options, true); + formatter.printHelp("Qpid", OPTIONS, true); return false; } } - @SuppressWarnings("static-access") - protected void setOptions(Options options) - { - Option help = new Option("h", "help", false, "print this message"); - Option version = new Option("v", "version", false, "print the version information and exit"); - Option configFile = - OptionBuilder.withArgName("file").hasArg().withDescription("use given configuration file").withLongOpt("config") - .create("c"); - Option port = - OptionBuilder.withArgName("port").hasArg() - .withDescription("listen on the specified port. Overrides any value in the config file") - .withLongOpt("port").create("p"); - - Option exclude0_10 = - OptionBuilder.withArgName("exclude-0-10").hasArg() - .withDescription("when listening on the specified port do not accept AMQP0-10 connections. The specified port must be one specified on the command line") - .withLongOpt("exclude-0-10").create(); - - Option exclude0_9_1 = - OptionBuilder.withArgName("exclude-0-9-1").hasArg() - .withDescription("when listening on the specified port do not accept AMQP0-9-1 connections. The specified port must be one specified on the command line") - .withLongOpt("exclude-0-9-1").create(); - - - Option exclude0_9 = - OptionBuilder.withArgName("exclude-0-9").hasArg() - .withDescription("when listening on the specified port do not accept AMQP0-9 connections. The specified port must be one specified on the command line") - .withLongOpt("exclude-0-9").create(); - - - Option exclude0_8 = - OptionBuilder.withArgName("exclude-0-8").hasArg() - .withDescription("when listening on the specified port do not accept AMQP0-8 connections. The specified port must be one specified on the command line") - .withLongOpt("exclude-0-8").create(); - - - Option mport = - OptionBuilder.withArgName("mport").hasArg() - .withDescription("listen on the specified management port. Overrides any value in the config file") - .withLongOpt("mport").create("m"); - - - Option bind = - OptionBuilder.withArgName("bind").hasArg() - .withDescription("bind to the specified address. Overrides any value in the config file") - .withLongOpt("bind").create("b"); - Option logconfig = - OptionBuilder.withArgName("logconfig").hasArg() - .withDescription("use the specified log4j xml configuration file. By " - + "default looks for a file named " + DEFAULT_LOG_CONFIG_FILENAME - + " in the same directory as the configuration file").withLongOpt("logconfig").create("l"); - Option logwatchconfig = - OptionBuilder.withArgName("logwatch").hasArg() - .withDescription("monitor the log file configuration file for changes. Units are seconds. " - + "Zero means do not check for changes.").withLongOpt("logwatch").create("w"); - - options.addOption(help); - options.addOption(version); - options.addOption(configFile); - options.addOption(logconfig); - options.addOption(logwatchconfig); - options.addOption(port); - options.addOption(exclude0_10); - options.addOption(exclude0_9_1); - options.addOption(exclude0_9); - options.addOption(exclude0_8); - options.addOption(mport); - options.addOption(bind); - } - - protected void execute() + protected void execute() throws Exception { - // note this understands either --help or -h. If an option only has a long name you can use that but if - // an option has a short name and a long name you must use the short name here. - if (commandLine.hasOption("h")) + BrokerOptions options = new BrokerOptions(); + String configFile = commandLine.getOptionValue(OPTION_CONFIG_FILE.getOpt()); + if(configFile != null) { - HelpFormatter formatter = new HelpFormatter(); - formatter.printHelp("Qpid", options, true); + options.setConfigFile(configFile); } - else if (commandLine.hasOption("v")) - { - String ver = QpidProperties.getVersionString(); - StringBuilder protocol = new StringBuilder("AMQP version(s) [major.minor]: "); - - boolean first = true; - for (ProtocolVersion pv : ProtocolVersion.getSupportedProtocolVersions()) - { - if (first) - { - first = false; - } - else - { - protocol.append(", "); - } - - protocol.append(pv.getMajorVersion()).append('-').append(pv.getMinorVersion()); - - } - - System.out.println(ver + " (" + protocol + ")"); - } - else + String logWatchConfig = commandLine.getOptionValue(OPTION_LOG_WATCH.getOpt()); + if(logWatchConfig != null) { - try - { - CurrentActor.set(new BrokerActor(new SystemOutMessageLogger())); - startup(); - CurrentActor.remove(); - } - catch (InitException e) - { - System.out.println("Initialisation Error : " + e.getMessage()); - shutdown(1); - } - catch (Throwable e) - { - System.out.println("Error initialising message broker: " + e); - e.printStackTrace(); - shutdown(1); - } + options.setLogWatchFrequency(Integer.parseInt(logWatchConfig)); } - } - - protected void shutdown(int status) - { - ApplicationRegistry.removeAll(); - System.exit(status); - } - protected void startup() throws Exception - { - final String QpidHome = System.getProperty(QPID_HOME); - final File defaultConfigFile = new File(QpidHome, DEFAULT_CONFIG_FILE); - final File configFile = new File(commandLine.getOptionValue("c", defaultConfigFile.getPath())); - if (!configFile.exists()) - { - String error = "File " + configFile + " could not be found. Check the file exists and is readable."; - - if (QpidHome == null) - { - error = error + "\nNote: " + QPID_HOME + " is not set."; - } - - throw new InitException(error, null); - } - else + String logConfig = commandLine.getOptionValue(OPTION_LOG_CONFIG_FILE.getOpt()); + if(logConfig != null) { - CurrentActor.get().message(BrokerMessages.CONFIG(configFile.getAbsolutePath())); + options.setLogConfigFile(logConfig); } - String logConfig = commandLine.getOptionValue("l"); - String logWatchConfig = commandLine.getOptionValue("w", "0"); - - int logWatchTime = 0; - try - { - logWatchTime = Integer.parseInt(logWatchConfig); - } - catch (NumberFormatException e) + String jmxPortRegistryServer = commandLine.getOptionValue(OPTION_JMX_PORT_REGISTRY_SERVER.getOpt()); + if(jmxPortRegistryServer != null) { - System.err.println("Log watch configuration value of " + logWatchConfig + " is invalid. Must be " - + "a non-negative integer. Using default of zero (no watching configured"); + options.setJmxPortRegistryServer(Integer.parseInt(jmxPortRegistryServer)); } - File logConfigFile; - if (logConfig != null) - { - logConfigFile = new File(logConfig); - configureLogging(logConfigFile, logWatchTime); - } - else + String jmxPortConnectorServer = commandLine.getOptionValue(OPTION_JMX_PORT_CONNECTOR_SERVER.getLongOpt()); + if(jmxPortConnectorServer != null) { - File configFileDirectory = configFile.getParentFile(); - logConfigFile = new File(configFileDirectory, DEFAULT_LOG_CONFIG_FILENAME); - configureLogging(logConfigFile, logWatchTime); + options.setJmxPortConnectorServer(Integer.parseInt(jmxPortConnectorServer)); } - ConfigurationFileApplicationRegistry config = new ConfigurationFileApplicationRegistry(configFile); - ServerConfiguration serverConfig = config.getConfiguration(); - updateManagementPort(serverConfig, commandLine.getOptionValue("m")); - - ApplicationRegistry.initialise(config); - - // We have already loaded the BrokerMessages class by this point so we - // need to refresh the locale setting incase we had a different value in - // the configuration. - BrokerMessages.reload(); - - // AR.initialise() sets and removes its own actor so we now need to set the actor - // for the remainder of the startup, and the default actor if the stack is empty - CurrentActor.set(new BrokerActor(config.getCompositeStartupMessageLogger())); - CurrentActor.setDefault(new BrokerActor(config.getRootMessageLogger())); - GenericActor.setDefaultMessageLogger(config.getRootMessageLogger()); - - - try + String bindAddr = commandLine.getOptionValue(OPTION_BIND.getOpt()); + if (bindAddr != null) { - configureLoggingManagementMBean(logConfigFile, logWatchTime); - - ConfigurationManagementMBean configMBean = new ConfigurationManagementMBean(); - configMBean.register(); - - ServerInformationMBean sysInfoMBean = - new ServerInformationMBean(QpidProperties.getBuildVersion(), QpidProperties.getReleaseVersion()); - sysInfoMBean.register(); - - - String[] portStr = commandLine.getOptionValues("p"); - - Set<Integer> ports = new HashSet<Integer>(); - Set<Integer> exclude_0_10 = new HashSet<Integer>(); - Set<Integer> exclude_0_9_1 = new HashSet<Integer>(); - Set<Integer> exclude_0_9 = new HashSet<Integer>(); - Set<Integer> exclude_0_8 = new HashSet<Integer>(); - - if(portStr == null || portStr.length == 0) - { - - parsePortList(ports, serverConfig.getPorts()); - parsePortList(exclude_0_10, serverConfig.getPortExclude010()); - parsePortList(exclude_0_9_1, serverConfig.getPortExclude091()); - parsePortList(exclude_0_9, serverConfig.getPortExclude09()); - parsePortList(exclude_0_8, serverConfig.getPortExclude08()); - - } - else - { - parsePortArray(ports, portStr); - parsePortArray(exclude_0_10, commandLine.getOptionValues("exclude-0-10")); - parsePortArray(exclude_0_9_1, commandLine.getOptionValues("exclude-0-9-1")); - parsePortArray(exclude_0_9, commandLine.getOptionValues("exclude-0-9")); - parsePortArray(exclude_0_8, commandLine.getOptionValues("exclude-0-8")); - - } - - - - - String bindAddr = commandLine.getOptionValue("b"); - if (bindAddr == null) - { - bindAddr = serverConfig.getBind(); - } - InetAddress bindAddress = null; - - - - if (bindAddr.equals("wildcard")) - { - bindAddress = new InetSocketAddress(0).getAddress(); - } - else - { - bindAddress = InetAddress.getByAddress(parseIP(bindAddr)); - } - - String hostName = bindAddress.getCanonicalHostName(); - - - String keystorePath = serverConfig.getKeystorePath(); - String keystorePassword = serverConfig.getKeystorePassword(); - String certType = serverConfig.getCertType(); - SSLContextFactory sslFactory = null; - - if (!serverConfig.getSSLOnly()) - { - - for(int port : ports) - { - - NetworkDriver driver = new MINANetworkDriver(); - - Set<VERSION> supported = EnumSet.allOf(VERSION.class); - - if(exclude_0_10.contains(port)) - { - supported.remove(VERSION.v0_10); - } - - if(exclude_0_9_1.contains(port)) - { - supported.remove(VERSION.v0_9_1); - } - if(exclude_0_9.contains(port)) - { - supported.remove(VERSION.v0_9); - } - if(exclude_0_8.contains(port)) - { - supported.remove(VERSION.v0_8); - } - - MultiVersionProtocolEngineFactory protocolEngineFactory = - new MultiVersionProtocolEngineFactory(hostName, supported); - - - - driver.bind(port, new InetAddress[]{bindAddress}, protocolEngineFactory, - serverConfig.getNetworkConfiguration(), null); - ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, port), - new QpidAcceptor(driver,"TCP")); - CurrentActor.get().message(BrokerMessages.LISTENING("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, serverConfig.getSSLPort()), - new QpidAcceptor(driver,"TCP")); - CurrentActor.get().message(BrokerMessages.LISTENING("TCP/SSL", serverConfig.getSSLPort())); - } - - CurrentActor.get().message(BrokerMessages.READY()); - - } - finally - { - // Startup is complete so remove the AR initialised Startup actor - CurrentActor.remove(); + options.setBind(bindAddr); } - - - } - - private void parsePortArray(Set<Integer> ports, String[] portStr) - throws InitException - { + String[] portStr = commandLine.getOptionValues(OPTION_PORT.getOpt()); if(portStr != null) { - for(int i = 0; i < portStr.length; i++) + parsePortArray(options, portStr, false); + for(ProtocolExclusion pe : ProtocolExclusion.values()) { - try - { - ports.add(Integer.parseInt(portStr[i])); - } - catch (NumberFormatException e) - { - throw new InitException("Invalid port: " + portStr[i], e); - } + parsePortArray(options, commandLine.getOptionValues(pe.getExcludeName()), pe); } } - } - private void parsePortList(Set<Integer> output, List input) - throws InitException - { - if(input != null) + String[] sslPortStr = commandLine.getOptionValues(OPTION_SSLPORT.getOpt()); + if(sslPortStr != null) { - for(Object port : input) + parsePortArray(options, sslPortStr, true); + for(ProtocolExclusion pe : ProtocolExclusion.values()) { - try - { - output.add(Integer.parseInt(String.valueOf(port))); - } - catch (NumberFormatException e) - { - throw new InitException("Invalid port: " + port, e); - } - } - } - } - - /** - * Update the configuration data with the management port. - * @param configuration - * @param managementPort The string from the command line - */ - private void updateManagementPort(ServerConfiguration configuration, String managementPort) - { - if (managementPort != null) - { - try - { - configuration.setJMXManagementPort(Integer.parseInt(managementPort)); - } - catch (NumberFormatException e) - { - _logger.warn("Invalid management port: " + managementPort + " will use:" + configuration.getJMXManagementPort(), e); + parsePortArray(options, commandLine.getOptionValues(pe.getExcludeName()), pe); } } + + startBroker(options); } - public static void main(String[] args) + protected void startBroker(final BrokerOptions options) throws Exception { - //if the -Dlog4j.configuration property has not been set, enable the init override - //to stop Log4J wondering off and picking up the first log4j.xml/properties file it - //finds from the classpath when we get the first Loggers - if(System.getProperty("log4j.configuration") == null) - { - System.setProperty("log4j.defaultInitOverride", "true"); - } - - //now that the override status is know, we can instantiate the Loggers - _logger = Logger.getLogger(Main.class); - - new Main(args); + Broker broker = new Broker(); + broker.startup(options); } - private byte[] parseIP(String address) throws Exception + protected void shutdown(final int status) { - char[] literalBuffer = address.toCharArray(); - int byteCount = 0; - int currByte = 0; - byte[] ip = new byte[IPV4_ADDRESS_LENGTH]; - for (int i = 0; i < literalBuffer.length; i++) - { - char currChar = literalBuffer[i]; - if ((currChar >= '0') && (currChar <= '9')) - { - currByte = (currByte * 10) + (Character.digit(currChar, 10) & 0xFF); - } - - if (currChar == IPV4_LITERAL_SEPARATOR || (i + 1 == literalBuffer.length)) - { - ip[byteCount++] = (byte) currByte; - currByte = 0; - } - } - - if (byteCount != 4) - { - throw new Exception("Invalid IP address: " + address); - } - return ip; + ApplicationRegistry.remove(); + System.exit(status); } - private void configureLogging(File logConfigFile, int logWatchTime) throws InitException, IOException + private static void parsePortArray(final BrokerOptions options,final Object[] ports, + final boolean ssl) throws InitException { - if (logConfigFile.exists() && logConfigFile.canRead()) + if(ports != null) { - CurrentActor.get().message(BrokerMessages.LOG_CONFIG(logConfigFile.getAbsolutePath())); - - if (logWatchTime > 0) + for(int i = 0; i < ports.length; i++) { - System.out.println("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every " - + logWatchTime + " seconds"); - // log4j expects the watch interval in milliseconds try { - QpidLog4JConfigurator.configureAndWatch(logConfigFile.getPath(), logWatchTime * 1000); - } - catch (Exception e) - { - throw new InitException(e.getMessage(),e); - } - } - else - { - try - { - QpidLog4JConfigurator.configure(logConfigFile.getPath()); + if(ssl) + { + options.addSSLPort(Integer.parseInt(String.valueOf(ports[i]))); + } + else + { + options.addPort(Integer.parseInt(String.valueOf(ports[i]))); + } } - catch (Exception e) + catch (NumberFormatException e) { - throw new InitException(e.getMessage(),e); + throw new InitException("Invalid port: " + ports[i], e); } } } - else - { - System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath()); - System.err.println("Using the fallback internal log4j.properties configuration"); + } - InputStream propsFile = this.getClass().getResourceAsStream("/log4j.properties"); - if(propsFile == null) - { - throw new IOException("Unable to load the fallback internal log4j.properties configuration file"); - } - else + private static void parsePortArray(final BrokerOptions options, final Object[] ports, + final ProtocolExclusion excludedProtocol) throws InitException + { + if(ports != null) + { + for(int i = 0; i < ports.length; i++) { try { - Properties fallbackProps = new Properties(); - fallbackProps.load(propsFile); - PropertyConfigurator.configure(fallbackProps); + options.addExcludedPort(excludedProtocol, + Integer.parseInt(String.valueOf(ports[i]))); } - finally + catch (NumberFormatException e) { - propsFile.close(); + throw new InitException("Invalid port for exclusion: " + ports[i], e); } } } } - - private void configureLoggingManagementMBean(File logConfigFile, int logWatchTime) throws Exception - { - LoggingManagementMBean blm = new LoggingManagementMBean(logConfigFile.getPath(),logWatchTime); - - blm.register(); - } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java b/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java new file mode 100644 index 0000000000..22d97d36dd --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java @@ -0,0 +1,73 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server; + +import java.util.HashMap; +import java.util.Map; + +public enum ProtocolExclusion +{ + v0_8("exclude-0-8","--exclude-0-8"), + v0_9("exclude-0-9", "--exclude-0-9"), + v0_9_1("exclude-0-9-1", "--exclude-0-9-1"), + v0_10("exclude-0-10", "--exclude-0-10"); + + private static final Map<String, ProtocolExclusion> MAP = new HashMap<String,ProtocolExclusion>(); + + static + { + for(ProtocolExclusion pe : ProtocolExclusion.values()) + { + MAP.put(pe.getArg(), pe); + } + } + + private String _arg; + private String _excludeName; + + private ProtocolExclusion(final String excludeName, final String arg) + { + _excludeName = excludeName; + _arg = arg; + } + + public String getArg() + { + return _arg; + } + + public String getExcludeName() + { + return _excludeName; + } + + public static ProtocolExclusion lookup(final String arg) + { + ProtocolExclusion ex = MAP.get(arg); + + if(ex == null) + { + throw new IllegalArgumentException(arg + " is not a valid protocol exclusion"); + } + + return ex; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index 7197ec8cdc..70fa39c71d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -20,6 +20,8 @@ package org.apache.qpid.server.configuration; +import static org.apache.qpid.transport.ConnectionSettings.WILDCARD_ADDRESS; + import java.io.File; import java.util.Collections; import java.util.HashMap; @@ -37,33 +39,28 @@ import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.configuration.SystemConfiguration; import org.apache.commons.configuration.XMLConfiguration; import org.apache.log4j.Logger; -import org.apache.qpid.server.configuration.management.ConfigurationManagementMBean; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.signal.SignalHandlerTask; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -import org.apache.qpid.transport.NetworkDriverConfiguration; - -import sun.misc.Signal; -import sun.misc.SignalHandler; -public class ServerConfiguration extends ConfigurationPlugin implements SignalHandler +public class ServerConfiguration extends ConfigurationPlugin { protected static final Logger _logger = Logger.getLogger(ServerConfiguration.class); // Default Configuration values - public static final int DEFAULT_BUFFER_READ_LIMIT_SIZE = 262144; - public static final int DEFAULT_BUFFER_WRITE_LIMIT_SIZE = 262144; - public static final boolean DEFAULT_BROKER_CONNECTOR_PROTECTIO_ENABLED = false; + public static final int DEFAULT_BUFFER_SIZE = 262144; public static final String DEFAULT_STATUS_UPDATES = "on"; public static final String SECURITY_CONFIG_RELOADED = "SECURITY CONFIGURATION RELOADED"; public static final int DEFAULT_FRAME_SIZE = 65536; public static final int DEFAULT_PORT = 5672; - public static final int DEFAULT_SSL_PORT = 8672; + public static final int DEFAULT_SSL_PORT = 5671; public static final long DEFAULT_HOUSEKEEPING_PERIOD = 30000L; - public static final int DEFAULT_JMXPORT = 8999; - + public static final int DEFAULT_JMXPORT_REGISTRYSERVER = 8999; + public static final int JMXPORT_CONNECTORSERVER_OFFSET = 100; + public static final String QPID_HOME = "QPID_HOME"; public static final String QPID_WORK = "QPID_WORK"; public static final String LIB_DIR = "lib"; @@ -75,19 +72,14 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa private File _configFile; private File _vhostsFile; - private Logger _log = Logger.getLogger(this.getClass()); - - private ConfigurationManagementMBean _mbean; - // Map of environment variables to config items private static final Map<String, String> envVarMap = new HashMap<String, String>(); // Configuration values to be read from the configuration file //todo Move all properties to static values to ensure system testing can be performed. - public static final String CONNECTOR_PROTECTIO_ENABLED = "connector.protectio.enabled"; - public static final String CONNECTOR_PROTECTIO_READ_BUFFER_LIMIT_SIZE = "connector.protectio.readBufferLimitSize"; - public static final String CONNECTOR_PROTECTIO_WRITE_BUFFER_LIMIT_SIZE = "connector.protectio.writeBufferLimitSize"; public static final String MGMT_CUSTOM_REGISTRY_SOCKET = "management.custom-registry-socket"; + public static final String MGMT_JMXPORT_REGISTRYSERVER = "management.jmxport.registryServer"; + public static final String MGMT_JMXPORT_CONNECTORSERVER = "management.jmxport.connectorServer"; public static final String STATUS_UPDATES = "status-updates"; public static final String ADVANCED_LOCALE = "advanced.locale"; @@ -95,9 +87,9 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa envVarMap.put("QPID_PORT", "connector.port"); envVarMap.put("QPID_ENABLEDIRECTBUFFERS", "advanced.enableDirectBuffers"); envVarMap.put("QPID_SSLPORT", "connector.ssl.port"); - envVarMap.put("QPID_NIO", "connector.qpidnio"); envVarMap.put("QPID_WRITEBIASED", "advanced.useWriteBiasedPool"); - envVarMap.put("QPID_JMXPORT", "management.jmxport"); + envVarMap.put("QPID_JMXPORT_REGISTRYSERVER", MGMT_JMXPORT_REGISTRYSERVER); + envVarMap.put("QPID_JMXPORT_CONNECTORSERVER", MGMT_JMXPORT_CONNECTORSERVER); envVarMap.put("QPID_FRAMESIZE", "advanced.framesize"); envVarMap.put("QPID_MSGAUTH", "security.msg-auth"); envVarMap.put("QPID_AUTOREGISTER", "auto_register"); @@ -131,7 +123,7 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa * Configuration Manager to be initialised in the Application Registry. * <p> * If using this ServerConfiguration via an ApplicationRegistry there is no - * need to explictly call {@link #initialise()} as this is done via the + * need to explicitly call {@link #initialise()} as this is done via the * {@link ApplicationRegistry#initialise()} method. * * @param configurationURL @@ -141,15 +133,26 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa { this(parseConfig(configurationURL)); _configFile = configurationURL; - try + + SignalHandlerTask hupReparseTask = new SignalHandlerTask() { - Signal sig = new sun.misc.Signal("HUP"); - sun.misc.Signal.handle(sig, this); - } - catch (Exception e) + public void handle() + { + try + { + reparseConfigFileSecuritySections(); + } + catch (ConfigurationException e) + { + _logger.error("Could not reload configuration file security sections", e); + } + } + }; + + if(!hupReparseTask.register("HUP")) { - _logger.error("Signal HUP not supported for OS: " + System.getProperty("os.name")); - // We're on something that doesn't handle SIGHUP, how sad, Windows. + _logger.info("Unable to register Signal HUP handler to reload security configuration."); + _logger.info("Signal HUP not supported for this OS / JVM combination - " + SignalHandlerTask.getPlatformDescription()); } } @@ -166,7 +169,7 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa * Configuration Manager to be initialised in the Application Registry. * <p> * If using this ServerConfiguration via an ApplicationRegistry there is no - * need to explictly call {@link #initialise()} as this is done via the + * need to explicitly call {@link #initialise()} as this is done via the * {@link ApplicationRegistry#initialise()} method. * * @param conf @@ -205,7 +208,53 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa @Override public void validateConfiguration() throws ConfigurationException { - //Currently doesn't do validation + // Support for security.jmx.access was removed when JMX access rights were incorporated into the main ACL. + // This ensure that users remove the element from their configuration file. + + if (getListValue("security.jmx.access").size() > 0) + { + String message = "Validation error : security/jmx/access is no longer a supported element within the configuration xml." + + (_configFile == null ? "" : " Configuration file : " + _configFile); + throw new ConfigurationException(message); + } + + if (getListValue("security.jmx.principal-database").size() > 0) + { + String message = "Validation error : security/jmx/principal-database is no longer a supported element within the configuration xml." + + (_configFile == null ? "" : " Configuration file : " + _configFile); + throw new ConfigurationException(message); + } + + if (getListValue("security.principal-databases.principal-database(0).class").size() > 0) + { + String message = "Validation error : security/principal-databases is no longer supported within the configuration xml." + + (_configFile == null ? "" : " Configuration file : " + _configFile); + throw new ConfigurationException(message); + } + + // QPID-3266. Tidy up housekeeping configuration option for scheduling frequency + if (contains("housekeeping.expiredMessageCheckPeriod")) + { + String message = "Validation error : housekeeping/expiredMessageCheckPeriod must be replaced by housekeeping/checkPeriod." + + (_configFile == null ? "" : " Configuration file : " + _configFile); + throw new ConfigurationException(message); + } + + // QPID-3517: Inconsistency in capitalisation in the SSL configuration keys used within the connector and management configuration + // sections. For the moment, continue to understand both but generate a deprecated warning if the less preferred keystore is used. + for (String key : new String[] {"management.ssl.keystorePath", + "management.ssl.keystorePassword," + + "connector.ssl.keystorePath", + "connector.ssl.keystorePassword"}) + { + if (contains(key)) + { + final String deprecatedXpath = key.replaceAll("\\.", "/"); + final String preferredXpath = deprecatedXpath.replaceAll("keystore", "keyStore"); + _logger.warn("Validation warning: " + deprecatedXpath + " is deprecated and must be replaced by " + preferredXpath + + (_configFile == null ? "" : " Configuration file : " + _configFile)); + } + } } /* @@ -371,7 +420,7 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa public final static Configuration flatConfig(File file) throws ConfigurationException { // We have to override the interpolate methods so that - // interpolation takes place accross the entirety of the + // interpolation takes place across the entirety of the // composite configuration. Without doing this each // configuration object only interpolates variables defined // inside itself. @@ -398,18 +447,6 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa return _configFile == null ? "" : _configFile.getAbsolutePath(); } - public void handle(Signal arg0) - { - try - { - reparseConfigFileSecuritySections(); - } - catch (ConfigurationException e) - { - _logger.error("Could not reload configuration file security sections", e); - } - } - public void reparseConfigFileSecuritySections() throws ConfigurationException { if (_configFile != null) @@ -453,14 +490,24 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa return System.getProperty(QPID_HOME); } - public void setJMXManagementPort(int mport) + public void setJMXPortRegistryServer(int registryServerPort) + { + getConfig().setProperty(MGMT_JMXPORT_REGISTRYSERVER, registryServerPort); + } + + public int getJMXPortRegistryServer() { - getConfig().setProperty("management.jmxport", mport); + return getIntValue(MGMT_JMXPORT_REGISTRYSERVER, DEFAULT_JMXPORT_REGISTRYSERVER); } - public int getJMXManagementPort() + public void setJMXPortConnectorServer(int connectorServerPort) { - return getIntValue("management.jmxport", DEFAULT_JMXPORT); + getConfig().setProperty(MGMT_JMXPORT_CONNECTORSERVER, connectorServerPort); + } + + public int getJMXConnectorServerPort() + { + return getIntValue(MGMT_JMXPORT_CONNECTORSERVER, getJMXPortRegistryServer() + JMXPORT_CONNECTORSERVER_OFFSET); } public boolean getUseCustomRMISocketFactory() @@ -503,58 +550,11 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa _virtualHosts.put(config.getName(), config); } - public List<String> getPrincipalDatabaseNames() - { - return getListValue("security.principal-databases.principal-database.name"); - } - - public List<String> getPrincipalDatabaseClass() - { - return getListValue("security.principal-databases.principal-database.class"); - } - - public List<String> getPrincipalDatabaseAttributeNames(int index) - { - String name = "security.principal-databases.principal-database(" + index + ")." + "attributes.attribute.name"; - return getListValue(name); - } - - public List<String> getPrincipalDatabaseAttributeValues(int index) - { - String name = "security.principal-databases.principal-database(" + index + ")." + "attributes.attribute.value"; - return getListValue(name); - } - - public List<String> getManagementPrincipalDBs() - { - return getListValue("security.jmx.principal-database"); - } - - public List<String> getManagementAccessList() - { - return getListValue("security.jmx.access"); - } - public int getFrameSize() { return getIntValue("advanced.framesize", DEFAULT_FRAME_SIZE); } - public boolean getProtectIOEnabled() - { - return getBooleanValue(CONNECTOR_PROTECTIO_ENABLED, DEFAULT_BROKER_CONNECTOR_PROTECTIO_ENABLED); - } - - public int getBufferReadLimit() - { - return getIntValue(CONNECTOR_PROTECTIO_READ_BUFFER_LIMIT_SIZE, DEFAULT_BUFFER_READ_LIMIT_SIZE); - } - - public int getBufferWriteLimit() - { - return getIntValue(CONNECTOR_PROTECTIO_WRITE_BUFFER_LIMIT_SIZE, DEFAULT_BUFFER_WRITE_LIMIT_SIZE); - } - public boolean getSynchedClocks() { return getBooleanValue("advanced.synced-clocks"); @@ -565,14 +565,10 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa return getBooleanValue("security.msg-auth"); } - public String getJMXPrincipalDatabase() - { - return getStringValue("security.jmx.principal-database"); - } - public String getManagementKeyStorePath() { - return getStringValue("management.ssl.keyStorePath"); + final String fallback = getStringValue("management.ssl.keystorePath"); + return getStringValue("management.ssl.keyStorePath", fallback); } public boolean getManagementSSLEnabled() @@ -582,7 +578,8 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa public String getManagementKeyStorePassword() { - return getStringValue("management.ssl.keyStorePassword"); + final String fallback = getStringValue("management.ssl.keystorePassword"); + return getStringValue("management.ssl.keyStorePassword", fallback); } public boolean getQueueAutoRegister() @@ -650,14 +647,14 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa return getLongValue("flowResumeCapacity", getCapacity()); } - public int getProcessors() + public int getConnectorProcessors() { return getIntValue("connector.processors", 4); } public List getPorts() { - return getListValue("connector.port", Collections.singletonList(DEFAULT_PORT)); + return getListValue("connector.port", Collections.<Integer>singletonList(DEFAULT_PORT)); } public List getPortExclude010() @@ -682,17 +679,17 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa public String getBind() { - return getStringValue("connector.bind", "wildcard"); + return getStringValue("connector.bind", WILDCARD_ADDRESS); } public int getReceiveBufferSize() { - return getIntValue("connector.socketReceiveBuffer", 32767); + return getIntValue("connector.socketReceiveBuffer", DEFAULT_BUFFER_SIZE); } public int getWriteBufferSize() { - return getIntValue("connector.socketWriteBuffer", 32767); + return getIntValue("connector.socketWriteBuffer", DEFAULT_BUFFER_SIZE); } public boolean getTcpNoDelay() @@ -715,31 +712,28 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa return getBooleanValue("connector.ssl.sslOnly"); } - public int getSSLPort() + public List getSSLPorts() { - return getIntValue("connector.ssl.port", DEFAULT_SSL_PORT); + return getListValue("connector.ssl.port", Collections.<Integer>singletonList(DEFAULT_SSL_PORT)); } - public String getKeystorePath() + public String getConnectorKeyStorePath() { - return getStringValue("connector.ssl.keystorePath", "none"); + final String fallback = getStringValue("connector.ssl.keystorePath"); // pre-0.13 broker supported this name. + return getStringValue("connector.ssl.keyStorePath", fallback); } - public String getKeystorePassword() + public String getConnectorKeyStorePassword() { - return getStringValue("connector.ssl.keystorePassword", "none"); + final String fallback = getStringValue("connector.ssl.keystorePassword"); // pre-0.13 brokers supported this name. + return getStringValue("connector.ssl.keyStorePassword", fallback); } - public String getCertType() + public String getConnectorCertType() { return getStringValue("connector.ssl.certType", "SunX509"); } - public boolean getQpidNIO() - { - return getBooleanValue("connector.qpidnio"); - } - public boolean getUseBiasedWrites() { return getBooleanValue("advanced.useWriteBiasedPool"); @@ -755,69 +749,44 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa getConfig().setProperty("virtualhosts.default", vhost); } - public void setHousekeepingExpiredMessageCheckPeriod(long value) + public void setHousekeepingCheckPeriod(long value) { - getConfig().setProperty("housekeeping.expiredMessageCheckPeriod", value); + getConfig().setProperty("housekeeping.checkPeriod", value); } public long getHousekeepingCheckPeriod() { - return getLongValue("housekeeping.checkPeriod", - getLongValue("housekeeping.expiredMessageCheckPeriod", - DEFAULT_HOUSEKEEPING_PERIOD)); + return getLongValue("housekeeping.checkPeriod", DEFAULT_HOUSEKEEPING_PERIOD); } - public NetworkDriverConfiguration getNetworkConfiguration() + public long getStatisticsSamplePeriod() { - return new NetworkDriverConfiguration() - { - - public Integer getTrafficClass() - { - return null; - } - - public Boolean getTcpNoDelay() - { - // Can't call parent getTcpNoDelay since it just calls this one - return getBooleanValue("connector.tcpNoDelay", true); - } - - public Integer getSoTimeout() - { - return null; - } - - public Integer getSoLinger() - { - return null; - } + return getConfig().getLong("statistics.sample.period", 5000L); + } - public Integer getSendBufferSize() - { - return getBufferWriteLimit(); - } + public boolean isStatisticsGenerationBrokerEnabled() + { + return getConfig().getBoolean("statistics.generation.broker", false); + } - public Boolean getReuseAddress() - { - return null; - } + public boolean isStatisticsGenerationVirtualhostsEnabled() + { + return getConfig().getBoolean("statistics.generation.virtualhosts", false); + } - public Integer getReceiveBufferSize() - { - return getBufferReadLimit(); - } + public boolean isStatisticsGenerationConnectionsEnabled() + { + return getConfig().getBoolean("statistics.generation.connections", false); + } - public Boolean getOOBInline() - { - return null; - } + public long getStatisticsReportingPeriod() + { + return getConfig().getLong("statistics.reporting.period", 0L); + } - public Boolean getKeepAlive() - { - return null; - } - }; + public boolean isStatisticsReportResetEnabled() + { + return getConfig().getBoolean("statistics.reporting.reset", false); } public int getMaxChannelCount() diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java new file mode 100644 index 0000000000..81dfcb4465 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.configuration; + +import org.apache.qpid.transport.NetworkTransportConfiguration; + +public class ServerNetworkTransportConfiguration implements NetworkTransportConfiguration +{ + private final ServerConfiguration _serverConfig; + private final int _port; + private final String _host; + private final String _transport; + + public ServerNetworkTransportConfiguration(final ServerConfiguration serverConfig, + final int port, final String host, + final String transport) + { + _serverConfig = serverConfig; + _port = port; + _host = host; + _transport = transport; + } + + public Boolean getTcpNoDelay() + { + return _serverConfig.getTcpNoDelay(); + } + + public Integer getSendBufferSize() + { + return _serverConfig.getWriteBufferSize(); + } + + public Integer getReceiveBufferSize() + { + return _serverConfig.getReceiveBufferSize(); + } + + public Integer getPort() + { + return _port; + } + + public String getHost() + { + return _host; + } + + public String getTransport() + { + return _transport; + } + + public Integer getConnectorProcessors() + { + return _serverConfig.getConnectorProcessors(); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java index d9d7083543..6729a5ce0f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java @@ -86,9 +86,9 @@ public class VirtualHostConfiguration extends ConfigurationPlugin return _name; } - public long getHousekeepingExpiredMessageCheckPeriod() + public long getHousekeepingCheckPeriod() { - return getLongValue("housekeeping.expiredMessageCheckPeriod", ApplicationRegistry.getInstance().getConfiguration().getHousekeepingCheckPeriod()); + return getLongValue("housekeeping.checkPeriod", ApplicationRegistry.getInstance().getConfiguration().getHousekeepingCheckPeriod()); } public String getAuthenticationDatabase() @@ -306,11 +306,45 @@ public class VirtualHostConfiguration extends ConfigurationPlugin @Override public void validateConfiguration() throws ConfigurationException { - //Currently doesn't do validation + // QPID-3249. Support for specifying authentication name at vhost level is no longer supported. + if (getListValue("security.authentication.name").size() > 0) + { + String message = "Validation error : security/authentication/name is no longer a supported element within the configuration xml." + + " It appears in virtual host definition : " + _name; + throw new ConfigurationException(message); + } + + // QPID-3266. Tidy up housekeeping configuration option for scheduling frequency + if (contains("housekeeping.expiredMessageCheckPeriod")) + { + String message = "Validation error : housekeeping/expiredMessageCheckPeriod must be replaced by housekeeping/checkPeriod." + + " It appears in virtual host definition : " + _name; + throw new ConfigurationException(message); + } } public int getHouseKeepingThreadCount() { return getIntValue("housekeeping.poolSize", Runtime.getRuntime().availableProcessors()); } + + public long getTransactionTimeoutOpenWarn() + { + return getLongValue("transactionTimeout.openWarn", 0L); + } + + public long getTransactionTimeoutOpenClose() + { + return getLongValue("transactionTimeout.openClose", 0L); + } + + public long getTransactionTimeoutIdleWarn() + { + return getLongValue("transactionTimeout.idleWarn", 0L); + } + + public long getTransactionTimeoutIdleClose() + { + return getLongValue("transactionTimeout.idleClose", 0L); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/ConfigurationPlugin.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/ConfigurationPlugin.java index 82b576ea51..b4f82649b0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/ConfigurationPlugin.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/plugins/ConfigurationPlugin.java @@ -24,6 +24,7 @@ import org.apache.commons.configuration.ConversionException; import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.ConfigurationManager; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; import java.util.Collections; import java.util.HashMap; @@ -138,10 +139,28 @@ public abstract class ConfigurationPlugin } } + offerRemainingConfigurationToOtherPlugins(path, configuration, elements); + + validateConfiguration(); + } + + private void offerRemainingConfigurationToOtherPlugins(String path, + Configuration configuration, Set<String> elements) throws ConfigurationException + { + final IApplicationRegistry appRegistry = safeGetApplicationRegistryInstance(); + + if (appRegistry == null) + { + // We see this happen during shutdown due to asynchronous reconfig using IO threads. + // Need to remove the responsibility for offering configuration to other class. + _logger.info("Cannot offer remaining config to other plugins, can't find app registry"); + return; + } + + final ConfigurationManager configurationManager = appRegistry.getConfigurationManager(); // Process the elements in the configuration for (String element : elements) { - ConfigurationManager configurationManager = ApplicationRegistry.getInstance().getConfigurationManager(); Configuration handled = element.length() == 0 ? configuration : configuration.subset(element); String configurationElement = element; @@ -162,8 +181,18 @@ public abstract class ConfigurationPlugin _pluginConfiguration.put(plugin.getClass().getName(), plugin); } } + } - validateConfiguration(); + private IApplicationRegistry safeGetApplicationRegistryInstance() + { + try + { + return ApplicationRegistry.getInstance(); + } + catch (IllegalStateException ise) + { + return null; + } } /** Helper method to print out list of keys in a {@link Configuration}. */ diff --git a/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java index bac751e0c8..1c01ce465d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java @@ -20,19 +20,21 @@ */ package org.apache.qpid.server.connection; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.log4j.Logger; -import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.common.Closeable; import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.protocol.AMQProtocolEngine; +import org.apache.qpid.transport.TransportException; public class ConnectionRegistry implements IConnectionRegistry, Closeable { - private List<AMQProtocolSession> _registry = new CopyOnWriteArrayList<AMQProtocolSession>(); + private List<AMQConnectionModel> _registry = new CopyOnWriteArrayList<AMQConnectionModel>(); private Logger _logger = Logger.getLogger(ConnectionRegistry.class); @@ -40,44 +42,46 @@ public class ConnectionRegistry implements IConnectionRegistry, Closeable { // None required } - - public void expireClosedChannels() - { - for (AMQProtocolSession connection : _registry) - { - connection.closeIfLingeringClosedChannels(); - } - } /** Close all of the currently open connections. */ public void close() { + _logger.debug("Closing connection registry :" + _registry.size() + " connections."); while (!_registry.isEmpty()) { - AMQProtocolSession connection = _registry.get(0); + AMQConnectionModel connection = _registry.get(0); + closeConnection(connection, AMQConstant.CONNECTION_FORCED, "Broker is shutting down"); + } + } - try - { - connection.closeConnection(0, new AMQConnectionException(AMQConstant.INTERNAL_ERROR, "Broker is shutting down", - 0, 0, - connection.getProtocolOutputConverter().getProtocolMajorVersion(), - connection.getProtocolOutputConverter().getProtocolMinorVersion(), - (Throwable) null), true); - } - catch (AMQException e) - { - _logger.warn("Error closing connection:" + e.getMessage()); - } + public void closeConnection(AMQConnectionModel connection, AMQConstant cause, String message) + { + try + { + connection.close(cause, message); + } + catch (TransportException e) + { + _logger.warn("Error closing connection:" + e.getMessage()); + } + catch (AMQException e) + { + _logger.warn("Error closing connection:" + e.getMessage()); } } - public void registerConnection(AMQProtocolSession connnection) + public void registerConnection(AMQConnectionModel connnection) { _registry.add(connnection); } - public void deregisterConnection(AMQProtocolSession connnection) + public void deregisterConnection(AMQConnectionModel connnection) { _registry.remove(connnection); } + + public List<AMQConnectionModel> getConnections() + { + return new ArrayList<AMQConnectionModel>(_registry); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java index 002269bbaa..b4f5bffa57 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java @@ -20,18 +20,23 @@ */ package org.apache.qpid.server.connection; -import org.apache.qpid.server.protocol.AMQProtocolSession; +import java.util.List; + import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.protocol.AMQConnectionModel; public interface IConnectionRegistry { - public void initialise(); public void close() throws AMQException; + + public void closeConnection(AMQConnectionModel connection, AMQConstant cause, String message); + + public List<AMQConnectionModel> getConnections(); - public void registerConnection(AMQProtocolSession connnection); - - public void deregisterConnection(AMQProtocolSession connnection); + public void registerConnection(AMQConnectionModel connnection); + public void deregisterConnection(AMQConnectionModel connnection); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index d0231e4d80..d693c6962b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -356,7 +356,7 @@ public abstract class AbstractExchange implements Exchange, Managable _receivedMessageCount.incrementAndGet(); _receivedMessageSize.addAndGet(message.getSize()); final ArrayList<? extends BaseQueue> queues = doRoute(message); - if(queues != null && !queues.isEmpty()) + if(!queues.isEmpty()) { _routedMessageCount.incrementAndGet(); _routedMessageSize.addAndGet(message.getSize()); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java index 7aeff2561e..0f1b709475 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java @@ -90,12 +90,12 @@ public abstract class AbstractExchangeMBean<T extends AbstractExchange> extends public String getObjectInstanceName() { - return _exchange.getNameShortString().toString(); + return ObjectName.quote(_exchange.getName()); } public String getName() { - return _exchange.getNameShortString().toString(); + return _exchange.getName(); } public String getExchangeType() diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java index 356a7f89b9..29a3611709 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java @@ -30,15 +30,12 @@ import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.message.InboundMessage; -import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.configuration.ExchangeConfig; import javax.management.JMException; import java.util.ArrayList; -import java.util.List; import java.util.Collection; -import java.util.concurrent.CopyOnWriteArrayList; public interface Exchange extends ExchangeReferrer, ExchangeConfig { @@ -67,7 +64,12 @@ public interface Exchange extends ExchangeReferrer, ExchangeConfig void close() throws AMQException; - + /** + * Returns a list of queues to which to route this message. If there are + * no queues the empty list must be returned. + * + * @return list of queues to which to route the message. + */ ArrayList<? extends BaseQueue> route(InboundMessage message); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java index 0e3a3894fe..d76b163fa1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java @@ -274,132 +274,6 @@ public class HeadersParser } - public static void main(String[] args) throws AMQFrameDecodingException - { - - FieldTable bindingTable = new FieldTable(); - - bindingTable.setString(new AMQShortString("x-match"),"all"); - bindingTable.setInteger("a",1); - bindingTable.setVoid(new AMQShortString("b")); - bindingTable.setString("c",""); - bindingTable.setInteger("d",4); - bindingTable.setInteger("e",1); - - - - FieldTable bindingTable2 = new FieldTable(); - bindingTable2.setString(new AMQShortString("x-match"),"all"); - bindingTable2.setInteger("a",1); - bindingTable2.setVoid(new AMQShortString("b")); - bindingTable2.setString("c",""); - bindingTable2.setInteger("d",4); - bindingTable2.setInteger("e",1); - bindingTable2.setInteger("f",1); - - - FieldTable table = new FieldTable(); - table.setInteger("a",1); - table.setInteger("b",2); - table.setString("c",""); - table.setInteger("d",4); - table.setInteger("e",1); - table.setInteger("f",1); - table.setInteger("h",1); - table.setInteger("i",1); - table.setInteger("j",1); - table.setInteger("k",1); - table.setInteger("l",1); - - org.apache.mina.common.ByteBuffer buffer = org.apache.mina.common.ByteBuffer.allocate( (int) table.getEncodedSize()); - EncodingUtils.writeFieldTableBytes(buffer, table); - buffer.flip(); - - FieldTable table2 = EncodingUtils.readFieldTable(buffer); - - - - FieldTable bindingTable3 = new FieldTable(); - bindingTable3.setString(new AMQShortString("x-match"),"any"); - bindingTable3.setInteger("a",1); - bindingTable3.setInteger("b",3); - - - FieldTable bindingTable4 = new FieldTable(); - bindingTable4.setString(new AMQShortString("x-match"),"any"); - bindingTable4.setVoid(new AMQShortString("a")); - - - FieldTable bindingTable5 = new FieldTable(); - bindingTable5.setString(new AMQShortString("x-match"),"all"); - bindingTable5.setString(new AMQShortString("h"),"hello"); - - for(int i = 0; i < 100; i++) - { - printMatches(new FieldTable[] {bindingTable5} , table2); - } - - - - } - - - - private static void printMatches(final FieldTable[] bindingKeys, final FieldTable routingKey) - { - HeadersMatcherDFAState sm = null; - Map<HeaderMatcherResult, String> resultMap = new HashMap<HeaderMatcherResult, String>(); - - HeadersParser parser = new HeadersParser(); - - for(int i = 0; i < bindingKeys.length; i++) - { - HeaderMatcherResult r = new HeaderMatcherResult(); - resultMap.put(r, bindingKeys[i].toString()); - - - if(i==0) - { - sm = parser.createStateMachine(bindingKeys[i], r); - } - else - { - sm = sm.mergeStateMachines(parser.createStateMachine(bindingKeys[i], r)); - } - } - - Collection<HeaderMatcherResult> results = null; - long beforeTime = System.currentTimeMillis(); - for(int i = 0; i < 1000000; i++) - { - routingKey.size(); - - assert sm != null; - results = sm.match(routingKey); - - } - long elapsed = System.currentTimeMillis() - beforeTime; - System.out.println("1000000 Iterations took: " + elapsed); - Collection<String> resultStrings = new ArrayList<String>(); - - assert results != null; - for(HeaderMatcherResult result : results) - { - resultStrings.add(resultMap.get(result)); - } - - final ArrayList<String> nonMatches = new ArrayList<String>(); - for(FieldTable key : bindingKeys) - { - nonMatches.add(key.toString()); - } - nonMatches.removeAll(resultStrings); - System.out.println("\""+routingKey+"\" matched with " + resultStrings + " DID NOT MATCH with " + nonMatches); - - - } - - public final static class KeyValuePair { public final HeaderKey _key; diff --git a/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java b/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java index fbc5387daf..2dff45c326 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java +++ b/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java @@ -38,6 +38,7 @@ import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.subscription.Subscription_0_10; import org.apache.qpid.server.transport.ServerSession; import org.apache.qpid.server.txn.AutoCommitTransaction; @@ -696,7 +697,7 @@ public class Bridge implements BridgeConfig //TODO Handle the passing of non-null Filters and Arguments here - Subscription_0_10 sub = new Subscription_0_10((ServerSession)session, + Subscription_0_10 sub = SubscriptionFactoryImpl.INSTANCE.createSubscription((ServerSession)session, _destination, MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, @@ -768,7 +769,7 @@ public class Bridge implements BridgeConfig //TODO Handle the passing of non-null Filters and Arguments here - Subscription_0_10 sub = new Subscription_0_10((ServerSession)session, + Subscription_0_10 sub = SubscriptionFactoryImpl.INSTANCE.createSubscription((ServerSession)session, _destination, MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, diff --git a/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java b/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java index fa2fb9ead1..f21158cd0c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java +++ b/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java @@ -258,7 +258,6 @@ public class BrokerLink implements LinkConfig, ConnectionListener _remoteFederationTag = UUID.fromString(_transport+":"+_host+":"+_port).toString(); } _qpidConnection.setSessionFactory(new SessionFactory()); - _qpidConnection.setAuthorizationID(_username == null ? "" : _username); updateState(State.ESTABLISHING, State.OPERATIONAL); diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java index 11fdeae2b1..9848f90ea9 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java @@ -37,8 +37,8 @@ import org.apache.qpid.server.queue.Filterable; public class PropertyExpression implements Expression { // Constants - defined the same as JMS - private static final int NON_PERSISTENT = 1; - private static final int PERSISTENT = 2; + private static enum JMSDeliveryMode { NON_PERSISTENT, PERSISTENT } + private static final int DEFAULT_PRIORITY = 4; private static final Logger _logger = org.apache.log4j.Logger.getLogger(PropertyExpression.class); @@ -172,13 +172,14 @@ public class PropertyExpression implements Expression { public Object evaluate(Filterable message) { - int mode = message.isPersistent() ? PERSISTENT : NON_PERSISTENT; + JMSDeliveryMode mode = message.isPersistent() ? JMSDeliveryMode.PERSISTENT : + JMSDeliveryMode.NON_PERSISTENT; if (_logger.isDebugEnabled()) { _logger.debug("JMSDeliveryMode is :" + mode); } - return mode; + return mode.toString(); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java index a5999711bc..765dee2878 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java @@ -144,7 +144,7 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic _logger.debug("Closing connection due to invalid selector"); MethodRegistry methodRegistry = protocolConnection.getMethodRegistry(); - AMQMethodBody responseBody = methodRegistry.createChannelCloseBody(AMQConstant.INVALID_ARGUMENT.getCode(), + AMQMethodBody responseBody = methodRegistry.createChannelCloseBody(AMQConstant.ARGUMENT_INVALID.getCode(), new AMQShortString(ise.getMessage()), body.getClazz(), body.getMethod()); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java index 790027f293..14ce85530e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java @@ -162,14 +162,7 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB } else { - sub = new GetNoAckSubscription(channel, - session, - null, - null, - false, - singleMessageCredit, - getDeliveryMethod, - getRecordMethod); + sub = SubscriptionFactoryImpl.INSTANCE.createBasicGetNoAckSubscription(channel, session, null, null, false, singleMessageCredit, getDeliveryMethod, getRecordMethod); } queue.registerSubscription(sub,false); @@ -180,27 +173,5 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB } - public static final class GetNoAckSubscription extends SubscriptionImpl.NoAckSubscription - { - public GetNoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, - AMQShortString consumerTag, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager, - ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) - throws AMQException - { - super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); - } - public boolean isTransient() - { - return true; - } - - public boolean wouldSuspend(QueueEntry msg) - { - return !getCreditManager().useCreditForMessage(msg.getMessage()); - } - - } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java index dade5d5f54..f8e4eab0b6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java @@ -68,5 +68,7 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener<Co ConnectionCloseOkBody responseBody = methodRegistry.createConnectionCloseOkBody(); session.writeFrame(responseBody.generateFrame(channelId)); + session.closeProtocolSession(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java index d4b79134a2..09f35da41d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java @@ -20,9 +20,9 @@ */ package org.apache.qpid.server.handler; + import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; - import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.ConnectionCloseBody; @@ -68,7 +68,7 @@ public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener } MethodRegistry methodRegistry = session.getMethodRegistry(); AuthenticationResult authResult = authMgr.authenticate(ss, body.getResponse()); - switch (authResult.status) + switch (authResult.getStatus()) { case ERROR: Exception cause = authResult.getCause(); @@ -88,7 +88,10 @@ public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener disposeSaslServer(session); break; case SUCCESS: - _logger.info("Connected as: " + ss.getAuthorizationID()); + if (_logger.isInfoEnabled()) + { + _logger.info("Connected as: " + UsernamePrincipal.getUsernamePrincipalFromSubject(authResult.getSubject())); + } stateManager.changeState(AMQState.CONNECTION_NOT_TUNED); ConnectionTuneBody tuneBody = @@ -96,13 +99,13 @@ public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener ConnectionStartOkMethodHandler.getConfiguredFrameSize(), ApplicationRegistry.getInstance().getConfiguration().getHeartBeatDelay()); session.writeFrame(tuneBody.generateFrame(0)); - session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID())); + session.setAuthorizedSubject(authResult.getSubject()); disposeSaslServer(session); break; case CONTINUE: stateManager.changeState(AMQState.CONNECTION_NOT_AUTH); - ConnectionSecureBody secureBody = methodRegistry.createConnectionSecureBody(authResult.challenge); + ConnectionSecureBody secureBody = methodRegistry.createConnectionSecureBody(authResult.getChallenge()); session.writeFrame(secureBody.generateFrame(0)); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java index 4442f969c4..2dd9a63540 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java @@ -65,7 +65,6 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< _logger.info("Locale selected: " + body.getLocale()); AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager(); - SaslServer ss = null; try { @@ -78,8 +77,7 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< session.setSaslServer(ss); - AuthenticationResult authResult = authMgr.authenticate(ss, body.getResponse()); - + final AuthenticationResult authResult = authMgr.authenticate(ss, body.getResponse()); //save clientProperties if (session.getClientProperties() == null) { @@ -88,7 +86,7 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< MethodRegistry methodRegistry = session.getMethodRegistry(); - switch (authResult.status) + switch (authResult.getStatus()) { case ERROR: Exception cause = authResult.getCause(); @@ -108,8 +106,11 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< break; case SUCCESS: - _logger.info("Connected as: " + ss.getAuthorizationID()); - session.setAuthorizedID(new UsernamePrincipal(ss.getAuthorizationID())); + if (_logger.isInfoEnabled()) + { + _logger.info("Connected as: " + UsernamePrincipal.getUsernamePrincipalFromSubject(authResult.getSubject())); + } + session.setAuthorizedSubject(authResult.getSubject()); stateManager.changeState(AMQState.CONNECTION_NOT_TUNED); @@ -121,7 +122,7 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< case CONTINUE: stateManager.changeState(AMQState.CONNECTION_NOT_AUTH); - ConnectionSecureBody secureBody = methodRegistry.createConnectionSecureBody(authResult.challenge); + ConnectionSecureBody secureBody = methodRegistry.createConnectionSecureBody(authResult.getChallenge()); session.writeFrame(secureBody.generateFrame(0)); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java index 8939cfa334..0cfed77f2e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java @@ -106,7 +106,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar else { queue = createQueue(queueName, body, virtualHost, protocolConnection); - queue.setPrincipalHolder(protocolConnection); + queue.setAuthorizationHolder(protocolConnection); if (queue.isDurable() && !queue.isAutoDelete()) { store.createQueue(queue, body.getArguments()); @@ -119,7 +119,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar if (body.getExclusive()) { queue.setExclusiveOwningSession(protocolConnection.getChannel(channelId)); - queue.setPrincipalHolder(protocolConnection); + queue.setAuthorizationHolder(protocolConnection); if(!body.getDurable()) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java b/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java index db2cc970b2..5e6a143d52 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java @@ -22,9 +22,11 @@ package org.apache.qpid.server.information.management; import java.io.IOException; +import org.apache.qpid.common.QpidProperties; import org.apache.qpid.management.common.mbeans.ServerInformation; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.registry.ApplicationRegistry; import javax.management.JMException; @@ -34,12 +36,15 @@ public class ServerInformationMBean extends AMQManagedObject implements ServerIn { private String buildVersion; private String productVersion; + private ApplicationRegistry registry; - public ServerInformationMBean(String buildVersion, String productVersion) throws JMException + public ServerInformationMBean(ApplicationRegistry applicationRegistry) throws JMException { super(ServerInformation.class, ServerInformation.TYPE); - this.buildVersion = buildVersion; - this.productVersion = productVersion; + + registry = applicationRegistry; + buildVersion = QpidProperties.getBuildVersion(); + productVersion = QpidProperties.getReleaseVersion(); } public String getObjectInstanceName() @@ -67,5 +72,75 @@ public class ServerInformationMBean extends AMQManagedObject implements ServerIn return productVersion; } + + public void resetStatistics() throws Exception + { + registry.resetStatistics(); + } + + public double getPeakMessageDeliveryRate() + { + return registry.getMessageDeliveryStatistics().getPeak(); + } + + public double getPeakDataDeliveryRate() + { + return registry.getDataDeliveryStatistics().getPeak(); + } + + public double getMessageDeliveryRate() + { + return registry.getMessageDeliveryStatistics().getRate(); + } + + public double getDataDeliveryRate() + { + return registry.getDataDeliveryStatistics().getRate(); + } + + public long getTotalMessagesDelivered() + { + return registry.getMessageDeliveryStatistics().getTotal(); + } + + public long getTotalDataDelivered() + { + return registry.getDataDeliveryStatistics().getTotal(); + } + + public double getPeakMessageReceiptRate() + { + return registry.getMessageReceiptStatistics().getPeak(); + } + + public double getPeakDataReceiptRate() + { + return registry.getDataReceiptStatistics().getPeak(); + } + + public double getMessageReceiptRate() + { + return registry.getMessageReceiptStatistics().getRate(); + } + + public double getDataReceiptRate() + { + return registry.getDataReceiptStatistics().getRate(); + } + + public long getTotalMessagesReceived() + { + return registry.getMessageReceiptStatistics().getTotal(); + } + + public long getTotalDataReceived() + { + return registry.getDataReceiptStatistics().getTotal(); + } + + public boolean isStatisticsEnabled() + { + return registry.isStatisticsEnabled(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java b/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java index 2825fa1b75..286fc78719 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java @@ -20,11 +20,15 @@ */ package org.apache.qpid.server.logging.actors; -import org.apache.qpid.server.logging.LogMessage; -import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.RootMessageLogger; +import java.security.AccessController; +import java.security.Principal; import java.text.MessageFormat; +import java.util.Set; + +import javax.management.remote.JMXPrincipal; +import javax.security.auth.Subject; /** * NOTE: This actor is not thread safe. @@ -40,16 +44,23 @@ import java.text.MessageFormat; */ public class ManagementActor extends AbstractActor { + /** + * Holds the principal name to display when principal subject is not available. + * <p> + * This is useful for cases when users invoke JMX operation over JConsole + * attached to the local JVM. + */ + private static final String UNKNOWN_PRINCIPAL = "N/A"; + String _lastThreadName = null; /** * LOG FORMAT for the ManagementActor, - * Uses a MessageFormat call to insert the requried values according to - * these indicies: + * Uses a MessageFormat call to insert the required values according to + * these indices: * - * 0 - Connection ID - * 1 - User ID - * 2 - IP + * 0 - User ID + * 1 - IP */ public static final String MANAGEMENT_FORMAT = "mng:{0}({1})"; @@ -75,19 +86,20 @@ public class ManagementActor extends AbstractActor _lastThreadName = currentName; // Management Thread names have this format. - //RMI TCP Connection(2)-169.24.29.116 + // RMI TCP Connection(2)-169.24.29.116 // This is true for both LocalAPI and JMX Connections // However to be defensive lets test. String[] split = currentName.split("\\("); if (split.length == 2) { - String connectionID = split[1].split("\\)")[0]; String ip = currentName.split("-")[1]; - - actor = MessageFormat.format(MANAGEMENT_FORMAT, - connectionID, - ip); + String principalName = getPrincipalName(); + if (principalName == null) + { + principalName = UNKNOWN_PRINCIPAL; + } + actor = MessageFormat.format(MANAGEMENT_FORMAT, principalName, ip); } else { @@ -105,6 +117,30 @@ public class ManagementActor extends AbstractActor } } + /** + * Returns current JMX principal name. + * + * @return principal name or null if principal can not be found + */ + protected String getPrincipalName() + { + String identity = null; + + // retrieve Subject from current AccessControlContext + final Subject subject = Subject.getSubject(AccessController.getContext()); + if (subject != null) + { + // retrieve JMXPrincipal from Subject + final Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class); + if (principals != null && !principals.isEmpty()) + { + final Principal principal = principals.iterator().next(); + identity = principal.getName(); + } + } + return identity; + } + public String getLogMessage() { updateLogString(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties index 6b83a7e7a5..5d1e85fe41 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties @@ -32,4 +32,7 @@ STOPPED = BRK-1005 : Stopped # 0 - path CONFIG = BRK-1006 : Using configuration : {0} # 0 - path -LOG_CONFIG = BRK-1007 : Using logging configuration : {0}
\ No newline at end of file +LOG_CONFIG = BRK-1007 : Using logging configuration : {0} + +STATS_DATA = BRK-1008 : {0,choice,0#delivered|1#received} : {1,number,#.###} kB/s peak : {2,number,#} bytes total +STATS_MSGS = BRK-1009 : {0,choice,0#delivered|1#received} : {1,number,#.###} msg/s peak : {2,number,#} msgs total
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties index 53bcd712f2..ed8c0d0ce9 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties @@ -28,3 +28,7 @@ PREFETCH_SIZE = CHN-1004 : Prefetch Size (bytes) {0,number} : Count {1,number} # 0 - queue causing flow control FLOW_ENFORCED = CHN-1005 : Flow Control Enforced (Queue {0}) FLOW_REMOVED = CHN-1006 : Flow Control Removed +# Channel Transactions +# 0 - time in milliseconds +OPEN_TXN = CHN-1007 : Open Transaction : {0,number} ms +IDLE_TXN = CHN-1008 : Idle Transaction : {0,number} ms diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Exchange_logmessages.properties b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Exchange_logmessages.properties index b9890d9f27..b25a6a7301 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Exchange_logmessages.properties +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Exchange_logmessages.properties @@ -21,4 +21,5 @@ # 0 - type # 1 - name CREATED = EXH-1001 : Create :[ Durable] Type: {0} Name: {1} -DELETED = EXH-1002 : Deleted
\ No newline at end of file +DELETED = EXH-1002 : Deleted +DISCARDMSG = EXH-1003 : Discarded Message : Name: {0} Routing Key: {1} diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/ManagementConsole_logmessages.properties b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/ManagementConsole_logmessages.properties index ab77476da2..ac77f674f2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/ManagementConsole_logmessages.properties +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/ManagementConsole_logmessages.properties @@ -30,4 +30,4 @@ STOPPED = MNG-1005 : Stopped # 0 - Path SSL_KEYSTORE = MNG-1006 : Using SSL Keystore : {0} OPEN = MNG-1007 : Open : User {0} -CLOSE = MNG-1008 : Close
\ No newline at end of file +CLOSE = MNG-1008 : Close : User {0}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/VirtualHost_logmessages.properties b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/VirtualHost_logmessages.properties index 66bbefacb0..3e640c7929 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/messages/VirtualHost_logmessages.properties +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/messages/VirtualHost_logmessages.properties @@ -20,4 +20,7 @@ # # 0 - name CREATED = VHT-1001 : Created : {0} -CLOSED = VHT-1002 : Closed
\ No newline at end of file +CLOSED = VHT-1002 : Closed + +STATS_DATA = VHT-1003 : {0} : {1,choice,0#delivered|1#received} : {2,number,#.###} kB/s peak : {3,number,#} bytes total +STATS_MSGS = VHT-1004 : {0} : {1,choice,0#delivered|1#received} : {2,number,#.###} msg/s peak : {3,number,#} msgs total`
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/ChannelLogSubject.java b/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/ChannelLogSubject.java index f28873940b..9b357403a8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/ChannelLogSubject.java +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/ChannelLogSubject.java @@ -47,7 +47,7 @@ public class ChannelLogSubject extends AbstractLogSubject */ setLogStringWithFormat(CHANNEL_FORMAT, session.getSessionID(), - session.getPrincipal().getName(), + session.getAuthorizedPrincipal().getName(), session.getRemoteAddress(), session.getVirtualHost().getName(), channel.getChannelId()); diff --git a/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/ConnectionLogSubject.java b/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/ConnectionLogSubject.java index a697029d24..c1c836f9b4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/ConnectionLogSubject.java +++ b/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/ConnectionLogSubject.java @@ -56,7 +56,7 @@ public class ConnectionLogSubject extends AbstractLogSubject { if (!_upToDate) { - if (_session.getPrincipal() != null) + if (_session.getAuthorizedPrincipal() != null) { if (_session.getVirtualHost() != null) { @@ -72,7 +72,7 @@ public class ConnectionLogSubject extends AbstractLogSubject */ _logString = "[" + MessageFormat.format(CONNECTION_FORMAT, _session.getSessionID(), - _session.getPrincipal().getName(), + _session.getAuthorizedPrincipal().getName(), _session.getRemoteAddress(), _session.getVirtualHost().getName()) + "] "; @@ -83,7 +83,7 @@ public class ConnectionLogSubject extends AbstractLogSubject { _logString = "[" + MessageFormat.format(USER_FORMAT, _session.getSessionID(), - _session.getPrincipal().getName(), + _session.getAuthorizedPrincipal().getName(), _session.getRemoteAddress()) + "] "; diff --git a/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java b/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java index 0a739af695..e44b8c41cb 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java +++ b/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java @@ -26,8 +26,9 @@ import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; import javax.management.StandardMBean; -import org.apache.qpid.AMQException; +import org.apache.log4j.Logger; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; /** * Provides implementation of the boilerplate ManagedObject interface. Most managed objects should find it useful @@ -36,10 +37,14 @@ import org.apache.qpid.server.registry.ApplicationRegistry; */ public abstract class DefaultManagedObject extends StandardMBean implements ManagedObject { + private static final Logger LOGGER = Logger.getLogger(ApplicationRegistry.class); + private Class<?> _managementInterface; private String _typeName; + private ManagedObjectRegistry _registry; + protected DefaultManagedObject(Class<?> managementInterface, String typeName) throws NotCompliantMBeanException { @@ -65,23 +70,26 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana public void register() throws JMException { - getManagedObjectRegistry().registerObject(this); + _registry = ApplicationRegistry.getInstance().getManagedObjectRegistry(); + _registry.registerObject(this); } - protected ManagedObjectRegistry getManagedObjectRegistry() - { - return ApplicationRegistry.getInstance().getManagedObjectRegistry(); - } - - public void unregister() throws AMQException + public void unregister() { try { - getManagedObjectRegistry().unregisterObject(this); + if(_registry != null) + { + _registry.unregisterObject(this); + } } catch (JMException e) { - throw new AMQException("Error unregistering managed object: " + this + ": " + e, e); + LOGGER.error("Error unregistering managed object: " + this + ": " + e, e); + } + finally + { + _registry = null; } } @@ -153,32 +161,4 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana return ""; } - protected static StringBuffer jmxEncode(StringBuffer jmxName, int attrPos) - { - for (int i = attrPos; i < jmxName.length(); i++) - { - if (jmxName.charAt(i) == ',') - { - jmxName.setCharAt(i, ';'); - } - else if (jmxName.charAt(i) == ':') - { - jmxName.setCharAt(i, '-'); - } - else if (jmxName.charAt(i) == '?' || - jmxName.charAt(i) == '*' || - jmxName.charAt(i) == '\\') - { - jmxName.insert(i, '\\'); - i++; - } - else if (jmxName.charAt(i) == '\n') - { - jmxName.insert(i, '\\'); - i++; - jmxName.setCharAt(i, 'n'); - } - } - return jmxName; - } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java index 0334a856c1..b44964f176 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java @@ -20,32 +20,6 @@ */ package org.apache.qpid.server.management; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.security.auth.database.PrincipalDatabase; -import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; - -import javax.management.JMException; -import javax.management.MBeanServer; -import javax.management.MBeanServerFactory; -import javax.management.ObjectName; -import javax.management.NotificationListener; -import javax.management.NotificationFilterSupport; -import javax.management.remote.JMXConnectorServer; -import javax.management.remote.JMXServiceURL; -import javax.management.remote.MBeanServerForwarder; -import javax.management.remote.JMXConnectionNotification; -import javax.management.remote.rmi.RMIConnectorServer; -import javax.management.remote.rmi.RMIJRMPServerImpl; -import javax.management.remote.rmi.RMIServerImpl; -import javax.rmi.ssl.SslRMIClientSocketFactory; -import javax.rmi.ssl.SslRMIServerSocketFactory; - import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -64,7 +38,31 @@ import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import java.rmi.server.UnicastRemoteObject; import java.util.HashMap; -import java.util.Map; + +import javax.management.JMException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.NotificationFilterSupport; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.remote.JMXConnectionNotification; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXServiceURL; +import javax.management.remote.MBeanServerForwarder; +import javax.management.remote.rmi.RMIConnectorServer; +import javax.management.remote.rmi.RMIJRMPServerImpl; +import javax.management.remote.rmi.RMIServerImpl; +import javax.rmi.ssl.SslRMIClientSocketFactory; +import javax.rmi.ssl.SslRMIServerSocketFactory; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator; /** * This class starts up an MBeanserver. If out of the box agent has been enabled then there are no @@ -74,15 +72,14 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry { private static final Logger _log = Logger.getLogger(JMXManagedObjectRegistry.class); - public static final String MANAGEMENT_PORT_CONFIG_PATH = "management.jmxport"; - public static final int MANAGEMENT_PORT_DEFAULT = 8999; - public static final int PORT_EXPORT_OFFSET = 100; - private final MBeanServer _mbeanServer; private JMXConnectorServer _cs; private Registry _rmiRegistry; private boolean _useCustomSocketFactory; + private final int _jmxPortRegistryServer; + private final int _jmxPortConnectorServer; + public JMXManagedObjectRegistry() throws AMQException { _log.info("Initialising managed object registry using platform MBean server"); @@ -95,8 +92,11 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry _mbeanServer = platformServer ? ManagementFactory.getPlatformMBeanServer() : MBeanServerFactory.createMBeanServer(ManagedObject.DOMAIN); - } + _jmxPortRegistryServer = appRegistry.getConfiguration().getJMXPortRegistryServer(); + _jmxPortConnectorServer = appRegistry.getConfiguration().getJMXConnectorServerPort(); + + } public void start() throws IOException, ConfigurationException { @@ -111,14 +111,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry } IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); - int port = appRegistry.getConfiguration().getJMXManagementPort(); - //retrieve the Principal Database assigned to JMX authentication duties - String jmxDatabaseName = appRegistry.getConfiguration().getJMXPrincipalDatabase(); - Map<String, PrincipalDatabase> map = appRegistry.getDatabaseManager().getDatabases(); - PrincipalDatabase db = map.get(jmxDatabaseName); - - HashMap<String,Object> env = new HashMap<String,Object>(); //Socket factories for the RMIConnectorServer, either default or SLL depending on configuration RMIClientSocketFactory csf; @@ -200,7 +193,8 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry //add a JMXAuthenticator implementation the env map to authenticate the RMI based JMX connector server RMIPasswordAuthenticator rmipa = new RMIPasswordAuthenticator(); - rmipa.setPrincipalDatabase(db); + rmipa.setAuthenticationManager(appRegistry.getAuthenticationManager()); + HashMap<String,Object> env = new HashMap<String,Object>(); env.put(JMXConnectorServer.AUTHENTICATOR, rmipa); /* @@ -211,14 +205,14 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry System.setProperty("java.rmi.server.randomIDs", "true"); if(_useCustomSocketFactory) { - _rmiRegistry = LocateRegistry.createRegistry(port, null, new CustomRMIServerSocketFactory()); + _rmiRegistry = LocateRegistry.createRegistry(_jmxPortRegistryServer, null, new CustomRMIServerSocketFactory()); } else { - _rmiRegistry = LocateRegistry.createRegistry(port, null, null); + _rmiRegistry = LocateRegistry.createRegistry(_jmxPortRegistryServer, null, null); } - CurrentActor.get().message(ManagementConsoleMessages.LISTENING("RMI Registry", port)); + CurrentActor.get().message(ManagementConsoleMessages.LISTENING("RMI Registry", _jmxPortRegistryServer)); /* * We must now create the RMI ConnectorServer manually, as the JMX Factory methods use RMI calls @@ -229,7 +223,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry * The registry is exported on the defined management port 'port'. We will export the RMIConnectorServer * on 'port +1'. Use of these two well-defined ports will ease any navigation through firewall's. */ - final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(port+PORT_EXPORT_OFFSET, csf, ssf, env); + final RMIServerImpl rmiConnectorServerStub = new RMIJRMPServerImpl(_jmxPortConnectorServer, csf, ssf, env); String localHost; try { @@ -241,9 +235,9 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry } final String hostname = localHost; final JMXServiceURL externalUrl = new JMXServiceURL( - "service:jmx:rmi://"+hostname+":"+(port+PORT_EXPORT_OFFSET)+"/jndi/rmi://"+hostname+":"+port+"/jmxrmi"); + "service:jmx:rmi://"+hostname+":"+(_jmxPortConnectorServer)+"/jndi/rmi://"+hostname+":"+_jmxPortRegistryServer+"/jmxrmi"); - final JMXServiceURL internalUrl = new JMXServiceURL("rmi", hostname, port+PORT_EXPORT_OFFSET); + final JMXServiceURL internalUrl = new JMXServiceURL("rmi", hostname, _jmxPortConnectorServer); _cs = new RMIConnectorServer(internalUrl, env, rmiConnectorServerStub, _mbeanServer) { @Override @@ -312,7 +306,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry _cs.start(); String connectorServer = (sslEnabled ? "SSL " : "") + "JMX RMIConnectorServer"; - CurrentActor.get().message(ManagementConsoleMessages.LISTENING(connectorServer, port + PORT_EXPORT_OFFSET)); + CurrentActor.get().message(ManagementConsoleMessages.LISTENING(connectorServer, _jmxPortConnectorServer)); CurrentActor.get().message(ManagementConsoleMessages.READY(false)); } @@ -407,7 +401,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry if (_rmiRegistry != null) { // Stopping the RMI registry - CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("RMI Registry", _cs.getAddress().getPort() - PORT_EXPORT_OFFSET)); + CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN("RMI Registry", _jmxPortRegistryServer)); try { UnicastRemoteObject.unexportObject(_rmiRegistry, false); diff --git a/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java index 964b5ed5a0..169195304c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java @@ -26,8 +26,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.security.AccessControlContext; import java.security.AccessController; -import java.security.Principal; -import java.util.Properties; import java.util.Set; import javax.management.Attribute; @@ -44,7 +42,6 @@ import javax.management.remote.MBeanServerForwarder; import javax.security.auth.Subject; import org.apache.log4j.Logger; -import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.ManagementActor; import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; import org.apache.qpid.server.registry.ApplicationRegistry; @@ -52,20 +49,15 @@ import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.access.Operation; /** - * This class can be used by the JMXConnectorServer as an InvocationHandler for the mbean operations. This implements - * the logic for allowing the users to invoke MBean operations and implements the restrictions for readOnly, readWrite - * and admin users. + * This class can be used by the JMXConnectorServer as an InvocationHandler for the mbean operations. It delegates + * JMX access decisions to the SecurityPlugin. */ public class MBeanInvocationHandlerImpl implements InvocationHandler, NotificationListener { private static final Logger _logger = Logger.getLogger(MBeanInvocationHandlerImpl.class); - public final static String ADMIN = "admin"; - public final static String READWRITE = "readwrite"; - public final static String READONLY = "readonly"; private final static String DELEGATE = "JMImplementation:type=MBeanServerDelegate"; private MBeanServer _mbs; - private static Properties _userRoles = new Properties(); private static ManagementActor _logActor; public static MBeanServerForwarder newProxyInstance() @@ -137,14 +129,13 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class); if (principals == null || principals.isEmpty()) { - throw new SecurityException("Access denied: no principal"); + throw new SecurityException("Access denied: no JMX principal"); } - - // Save the principal - Principal principal = principals.iterator().next(); - SecurityManager.setThreadPrincipal(principal); - - // Get the component, type and impact, which may be null + + // Save the subject + SecurityManager.setThreadSubject(subject); + + // Get the component, type and impact, which may be null String type = getType(method, args); String vhost = getVirtualHost(method, args); int impact = getImpact(method, args); @@ -213,6 +204,20 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati ObjectName object = (ObjectName) args[0]; String vhost = object.getKeyProperty("VirtualHost"); + if(vhost != null) + { + try + { + //if the name is quoted in the ObjectName, unquote it + vhost = ObjectName.unquote(vhost); + } + catch(IllegalArgumentException e) + { + //ignore, this just means the name is not quoted + //and can be left unchanged + } + } + return vhost; } return null; @@ -272,7 +277,7 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati } catch (JMException ex) { - ex.printStackTrace(); + _logger.error("Unable to determine mbean impact for method : " + mbeanMethod, ex); } } @@ -308,7 +313,7 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati else if (notification.getType().equals(JMXConnectionNotification.CLOSED) || notification.getType().equals(JMXConnectionNotification.FAILED)) { - _logActor.message(ManagementConsoleMessages.CLOSE()); + _logActor.message(ManagementConsoleMessages.CLOSE(user)); } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/message/ContentHeaderBodyAdapter.java b/java/broker/src/main/java/org/apache/qpid/server/message/ContentHeaderBodyAdapter.java index 194835ac02..84a1642578 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/message/ContentHeaderBodyAdapter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/message/ContentHeaderBodyAdapter.java @@ -37,7 +37,7 @@ public class ContentHeaderBodyAdapter implements AMQMessageHeader private BasicContentHeaderProperties getProperties() { - return (BasicContentHeaderProperties) _contentHeaderBody.properties; + return (BasicContentHeaderProperties) _contentHeaderBody.getProperties(); } public String getCorrelationId() diff --git a/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData.java b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData.java index 30bea7b6e6..5992e42fb7 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData.java +++ b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData.java @@ -29,7 +29,10 @@ import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.store.StorableMessageMetaData; import org.apache.qpid.server.store.MessageMetaDataType; import org.apache.qpid.AMQException; +import org.apache.qpid.server.util.ByteBufferInputStream; +import org.apache.qpid.server.util.ByteBufferOutputStream; +import java.io.*; import java.nio.ByteBuffer; import java.util.Set; @@ -120,38 +123,38 @@ public class MessageMetaData implements StorableMessageMetaData return size; } + public int writeToBuffer(int offset, ByteBuffer dest) { - ByteBuffer src = ByteBuffer.allocate((int)getStorableSize()); - - org.apache.mina.common.ByteBuffer minaSrc = org.apache.mina.common.ByteBuffer.wrap(src); - EncodingUtils.writeInteger(minaSrc, _contentHeaderBody.getSize()); - _contentHeaderBody.writePayload(minaSrc); - EncodingUtils.writeShortStringBytes(minaSrc, _messagePublishInfo.getExchange()); - EncodingUtils.writeShortStringBytes(minaSrc, _messagePublishInfo.getRoutingKey()); - byte flags = 0; - if(_messagePublishInfo.isMandatory()) - { - flags |= MANDATORY_FLAG; - } - if(_messagePublishInfo.isImmediate()) + int oldPosition = dest.position(); + try { - flags |= IMMEDIATE_FLAG; + + DataOutputStream dataOutputStream = new DataOutputStream(new ByteBufferOutputStream(dest)); + EncodingUtils.writeInteger(dataOutputStream, _contentHeaderBody.getSize()); + _contentHeaderBody.writePayload(dataOutputStream); + EncodingUtils.writeShortStringBytes(dataOutputStream, _messagePublishInfo.getExchange()); + EncodingUtils.writeShortStringBytes(dataOutputStream, _messagePublishInfo.getRoutingKey()); + byte flags = 0; + if(_messagePublishInfo.isMandatory()) + { + flags |= MANDATORY_FLAG; + } + if(_messagePublishInfo.isImmediate()) + { + flags |= IMMEDIATE_FLAG; + } + dest.put(flags); + dest.putLong(_arrivalTime); + } - EncodingUtils.writeByte(minaSrc, flags); - EncodingUtils.writeLong(minaSrc,_arrivalTime); - src.position(minaSrc.position()); - src.flip(); - src.position(offset); - src = src.slice(); - if(dest.remaining() < src.limit()) + catch (IOException e) { - src.limit(dest.remaining()); + // This shouldn't happen as we are not actually using anything that can throw an IO Exception + throw new RuntimeException(e); } - dest.put(src); - - return src.limit(); + return dest.position()-oldPosition; } public int getContentSize() @@ -161,7 +164,7 @@ public class MessageMetaData implements StorableMessageMetaData public boolean isPersistent() { - BasicContentHeaderProperties properties = (BasicContentHeaderProperties) (_contentHeaderBody.properties); + BasicContentHeaderProperties properties = (BasicContentHeaderProperties) (_contentHeaderBody.getProperties()); return properties.getDeliveryMode() == BasicContentHeaderProperties.PERSISTENT; } @@ -173,14 +176,15 @@ public class MessageMetaData implements StorableMessageMetaData { try { - org.apache.mina.common.ByteBuffer minaSrc = org.apache.mina.common.ByteBuffer.wrap(buf); - int size = EncodingUtils.readInteger(minaSrc); - ContentHeaderBody chb = ContentHeaderBody.createFromBuffer(minaSrc, size); - final AMQShortString exchange = EncodingUtils.readAMQShortString(minaSrc); - final AMQShortString routingKey = EncodingUtils.readAMQShortString(minaSrc); + ByteBufferInputStream bbis = new ByteBufferInputStream(buf); + DataInputStream dais = new DataInputStream(bbis); + int size = EncodingUtils.readInteger(dais); + ContentHeaderBody chb = ContentHeaderBody.createFromBuffer(dais, size); + final AMQShortString exchange = EncodingUtils.readAMQShortString(dais); + final AMQShortString routingKey = EncodingUtils.readAMQShortString(dais); - final byte flags = EncodingUtils.readByte(minaSrc); - long arrivalTime = EncodingUtils.readLong(minaSrc); + final byte flags = EncodingUtils.readByte(dais); + long arrivalTime = EncodingUtils.readLong(dais); MessagePublishInfo publishBody = new MessagePublishInfo() @@ -216,6 +220,10 @@ public class MessageMetaData implements StorableMessageMetaData { throw new RuntimeException(e); } + catch (IOException e) + { + throw new RuntimeException(e); + } } }; @@ -229,7 +237,7 @@ public class MessageMetaData implements StorableMessageMetaData { private BasicContentHeaderProperties getProperties() { - return (BasicContentHeaderProperties) getContentHeaderBody().properties; + return (BasicContentHeaderProperties) getContentHeaderBody().getProperties(); } public String getCorrelationId() diff --git a/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_0_10.java b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_0_10.java index cf8ae2166c..2297e4200d 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_0_10.java +++ b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_0_10.java @@ -34,7 +34,7 @@ import org.apache.qpid.transport.codec.BBDecoder; import java.nio.ByteBuffer; import java.lang.ref.SoftReference; -public class MessageMetaData_0_10 implements StorableMessageMetaData +public class MessageMetaData_0_10 implements StorableMessageMetaData, InboundMessage { private Header _header; private DeliveryProperties _deliveryProps; @@ -194,6 +194,12 @@ public class MessageMetaData_0_10 implements StorableMessageMetaData return _deliveryProps == null ? 0L : _deliveryProps.getExpiration(); } + public boolean isRedelivered() + { + // The *Message* is never redelivered, only queue entries are... + return false; + } + public long getArrivalTime() { return _arrivalTime; @@ -239,4 +245,6 @@ public class MessageMetaData_0_10 implements StorableMessageMetaData } } + + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java index 2cebec373e..3970e5a2d4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java @@ -26,6 +26,7 @@ */
package org.apache.qpid.server.output.amqp0_8;
+import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.message.AMQMessage;
import org.apache.qpid.server.queue.QueueEntry;
@@ -34,22 +35,18 @@ import org.apache.qpid.server.output.HeaderPropertiesConverter; import org.apache.qpid.server.message.MessageContentSource;
import org.apache.qpid.server.message.MessageTransferMessage;
import org.apache.qpid.framing.*;
-import org.apache.qpid.framing.amqp_8_0.BasicGetBodyImpl;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
import org.apache.qpid.AMQException;
import org.apache.qpid.transport.DeliveryProperties;
-import java.nio.ByteBuffer;
+import java.io.DataOutputStream;
+import java.io.IOException;
public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
{
private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v8_0);
- private static final ProtocolVersionMethodConverter PROTOCOL_CONVERTER =
- METHOD_REGISTRY.getProtocolVersionMethodConverter();
-
public static Factory getInstanceFactory()
{
return new Factory()
@@ -62,6 +59,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter };
}
+
private final AMQProtocolSession _protocolSession;
private ProtocolOutputConverterImpl(AMQProtocolSession session)
@@ -78,10 +76,11 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter public void writeDeliver(QueueEntry entry, int channelId, long deliveryTag, AMQShortString consumerTag)
throws AMQException
{
- AMQDataBlock deliver = createEncodedDeliverFrame(entry, channelId, deliveryTag, consumerTag);
- writeMessageDelivery(entry.getMessage(), getContentHeaderBody(entry), channelId, deliver);
+ AMQBody deliverBody = createEncodedDeliverBody(entry, deliveryTag, consumerTag);
+ writeMessageDelivery(entry, channelId, deliverBody);
}
+
private ContentHeaderBody getContentHeaderBody(QueueEntry entry)
throws AMQException
{
@@ -93,65 +92,120 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter {
final MessageTransferMessage message = (MessageTransferMessage) entry.getMessage();
BasicContentHeaderProperties props = HeaderPropertiesConverter.convert(message);
- ContentHeaderBody chb = new ContentHeaderBody(props, BasicGetBodyImpl.CLASS_ID);
- chb.bodySize = message.getSize();
+ ContentHeaderBody chb = new ContentHeaderBody(props, org.apache.qpid.framing.amqp_8_0.BasicGetBodyImpl.CLASS_ID);
+ chb.bodySize = message.getSize();
return chb;
}
}
- public void writeGetOk(QueueEntry entry, int channelId, long deliveryTag, int queueSize) throws AMQException
+ private void writeMessageDelivery(QueueEntry entry, int channelId, AMQBody deliverBody)
+ throws AMQException
{
- AMQDataBlock deliver = createEncodedGetOkFrame(entry, channelId, deliveryTag, queueSize);
- writeMessageDelivery(entry.getMessage(), getContentHeaderBody(entry), channelId, deliver);
+ writeMessageDelivery(entry.getMessage(), getContentHeaderBody(entry), channelId, deliverBody);
}
- private void writeMessageDelivery(MessageContentSource message, ContentHeaderBody chb, int channelId, AMQDataBlock deliver)
+ private void writeMessageDelivery(MessageContentSource message, ContentHeaderBody contentHeaderBody, int channelId, AMQBody deliverBody)
throws AMQException
{
- AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId, chb);
-
+ int bodySize = (int) message.getSize();
- final int bodySize = (int) message.getSize();
if(bodySize == 0)
{
- SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
- contentHeader);
+ SmallCompositeAMQBodyBlock compositeBlock = new SmallCompositeAMQBodyBlock(channelId, deliverBody,
+ contentHeaderBody);
+
writeFrame(compositeBlock);
}
else
{
int maxBodySize = (int) getProtocolSession().getMaxFrameSize() - AMQFrame.getFrameOverhead();
- final int capacity = bodySize > maxBodySize ? maxBodySize : bodySize;
- ByteBuffer buf = ByteBuffer.allocate(capacity);
- int writtenSize = 0;
+ int capacity = bodySize > maxBodySize ? maxBodySize : bodySize;
+
+ int writtenSize = capacity;
+
+ AMQBody firstContentBody = new MessageContentSourceBody(message,0,capacity);
- writtenSize += message.getContent(buf, writtenSize);
- buf.flip();
- AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_CONVERTER.convertToBody(buf));
- AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
- CompositeAMQDataBlock compositeBlock = new CompositeAMQDataBlock(blocks);
+ CompositeAMQBodyBlock
+ compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody);
writeFrame(compositeBlock);
while(writtenSize < bodySize)
{
- buf = java.nio.ByteBuffer.allocate(capacity);
- writtenSize += message.getContent(buf, writtenSize);
- buf.flip();
- writeFrame(new AMQFrame(channelId, PROTOCOL_CONVERTER.convertToBody(buf)));
+ capacity = bodySize - writtenSize > maxBodySize ? maxBodySize : bodySize - writtenSize;
+ MessageContentSourceBody body = new MessageContentSourceBody(message, writtenSize, capacity);
+ writtenSize += capacity;
+
+ writeFrame(new AMQFrame(channelId, body));
}
+ }
+ }
+ private class MessageContentSourceBody implements AMQBody
+ {
+ public static final byte TYPE = 3;
+ private int _length;
+ private MessageContentSource _message;
+ private int _offset;
+
+ public MessageContentSourceBody(MessageContentSource message, int offset, int length)
+ {
+ _message = message;
+ _offset = offset;
+ _length = length;
+ }
+
+ public byte getFrameType()
+ {
+ return TYPE;
}
+
+ public int getSize()
+ {
+ return _length;
+ }
+
+ public void writePayload(DataOutputStream buffer) throws IOException
+ {
+ byte[] data = new byte[_length];
+
+ _message.getContent(java.nio.ByteBuffer.wrap(data), _offset);
+
+ buffer.write(data);
+ }
+
+ public void handle(int channelId, AMQVersionAwareProtocolSession amqProtocolSession) throws AMQException
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody)
+ {
+
+ AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
+ contentHeaderBody);
+ return contentHeader;
}
- private AMQDataBlock createEncodedDeliverFrame(QueueEntry entry, int channelId, long deliveryTag, AMQShortString consumerTag)
+ public void writeGetOk(QueueEntry entry, int channelId, long deliveryTag, int queueSize) throws AMQException
+ {
+ AMQBody deliver = createEncodedGetOkBody(entry, deliveryTag, queueSize);
+ writeMessageDelivery(entry, channelId, deliver);
+ }
+
+
+ private AMQBody createEncodedDeliverBody(QueueEntry entry,
+ final long deliveryTag,
+ final AMQShortString consumerTag)
throws AMQException
{
+
final AMQShortString exchangeName;
final AMQShortString routingKey;
@@ -172,21 +226,58 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter final boolean isRedelivered = entry.isRedelivered();
+ final AMQBody returnBlock = new AMQBody()
+ {
+
+ public AMQBody _underlyingBody;
- BasicDeliverBody deliverBody =
- METHOD_REGISTRY.createBasicDeliverBody(consumerTag,
- deliveryTag,
- isRedelivered,
- exchangeName,
- routingKey);
+ public AMQBody createAMQBody()
+ {
+ return METHOD_REGISTRY.createBasicDeliverBody(consumerTag,
+ deliveryTag,
+ isRedelivered,
+ exchangeName,
+ routingKey);
- AMQFrame deliverFrame = deliverBody.generateFrame(channelId);
- return deliverFrame;
+
+
+ }
+
+ public byte getFrameType()
+ {
+ return AMQMethodBody.TYPE;
+ }
+
+ public int getSize()
+ {
+ if(_underlyingBody == null)
+ {
+ _underlyingBody = createAMQBody();
+ }
+ return _underlyingBody.getSize();
+ }
+
+ public void writePayload(DataOutputStream buffer) throws IOException
+ {
+ if(_underlyingBody == null)
+ {
+ _underlyingBody = createAMQBody();
+ }
+ _underlyingBody.writePayload(buffer);
+ }
+
+ public void handle(final int channelId, final AMQVersionAwareProtocolSession amqMinaProtocolSession)
+ throws AMQException
+ {
+ throw new AMQException("This block should never be dispatched!");
+ }
+ };
+ return returnBlock;
}
- private AMQDataBlock createEncodedGetOkFrame(QueueEntry entry, int channelId, long deliveryTag, int queueSize)
+ private AMQBody createEncodedGetOkBody(QueueEntry entry, long deliveryTag, int queueSize)
throws AMQException
{
final AMQShortString exchangeName;
@@ -215,9 +306,8 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter exchangeName,
routingKey,
queueSize);
- AMQFrame getOkFrame = getOkBody.generateFrame(channelId);
- return getOkFrame;
+ return getOkBody;
}
public byte getProtocolMinorVersion()
@@ -230,31 +320,28 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter return getProtocolSession().getProtocolMajorVersion();
}
- private AMQDataBlock createEncodedReturnFrame(MessagePublishInfo messagePublishInfo, int channelId, int replyCode, AMQShortString replyText) throws AMQException
+ private AMQBody createEncodedReturnFrame(MessagePublishInfo messagePublishInfo,
+ int replyCode,
+ AMQShortString replyText) throws AMQException
{
+
BasicReturnBody basicReturnBody =
METHOD_REGISTRY.createBasicReturnBody(replyCode,
- replyText,
- messagePublishInfo.getExchange(),
- messagePublishInfo.getRoutingKey());
- AMQFrame returnFrame = basicReturnBody.generateFrame(channelId);
+ replyText,
+ messagePublishInfo.getExchange(),
+ messagePublishInfo.getRoutingKey());
- return returnFrame;
+
+ return basicReturnBody;
}
- public void writeReturn(MessagePublishInfo messagePublishInfo,
- ContentHeaderBody header,
- MessageContentSource content,
- int channelId,
- int replyCode,
- AMQShortString replyText)
+ public void writeReturn(MessagePublishInfo messagePublishInfo, ContentHeaderBody header, MessageContentSource message, int channelId, int replyCode, AMQShortString replyText)
throws AMQException
{
- AMQDataBlock returnFrame = createEncodedReturnFrame(messagePublishInfo, channelId, replyCode, replyText);
-
- writeMessageDelivery(content, header, channelId, returnFrame);
+ AMQBody returnFrame = createEncodedReturnFrame(messagePublishInfo, replyCode, replyText);
+ writeMessageDelivery(message, header, channelId, returnFrame);
}
@@ -266,8 +353,68 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter public void confirmConsumerAutoClose(int channelId, AMQShortString consumerTag)
{
+
BasicCancelOkBody basicCancelOkBody = METHOD_REGISTRY.createBasicCancelOkBody(consumerTag);
writeFrame(basicCancelOkBody.generateFrame(channelId));
}
+
+
+ public static final class CompositeAMQBodyBlock extends AMQDataBlock
+ {
+ public static final int OVERHEAD = 3 * AMQFrame.getFrameOverhead();
+
+ private final AMQBody _methodBody;
+ private final AMQBody _headerBody;
+ private final AMQBody _contentBody;
+ private final int _channel;
+
+
+ public CompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody, AMQBody contentBody)
+ {
+ _channel = channel;
+ _methodBody = methodBody;
+ _headerBody = headerBody;
+ _contentBody = contentBody;
+
+ }
+
+ public long getSize()
+ {
+ return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize();
+ }
+
+ public void writePayload(DataOutputStream buffer) throws IOException
+ {
+ AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody);
+ }
+ }
+
+ public static final class SmallCompositeAMQBodyBlock extends AMQDataBlock
+ {
+ public static final int OVERHEAD = 2 * AMQFrame.getFrameOverhead();
+
+ private final AMQBody _methodBody;
+ private final AMQBody _headerBody;
+ private final int _channel;
+
+
+ public SmallCompositeAMQBodyBlock(int channel, AMQBody methodBody, AMQBody headerBody)
+ {
+ _channel = channel;
+ _methodBody = methodBody;
+ _headerBody = headerBody;
+
+ }
+
+ public long getSize()
+ {
+ return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ;
+ }
+
+ public void writePayload(DataOutputStream buffer) throws IOException
+ {
+ AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody);
+ }
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java index 319b5cc7bd..aef3483282 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java @@ -20,9 +20,6 @@ package org.apache.qpid.server.output.amqp0_9; *
*/
-
-import org.apache.mina.common.ByteBuffer;
-
import org.apache.qpid.server.output.ProtocolOutputConverter;
import org.apache.qpid.server.output.HeaderPropertiesConverter;
import org.apache.qpid.server.protocol.AMQProtocolSession;
@@ -38,11 +35,13 @@ import org.apache.qpid.AMQException; import org.apache.qpid.transport.DeliveryProperties;
import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
{
private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_9);
- private static final ProtocolVersionMethodConverter
- PROTOCOL_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter();
public static Factory getInstanceFactory()
@@ -121,15 +120,12 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter int maxBodySize = (int) getProtocolSession().getMaxFrameSize() - AMQFrame.getFrameOverhead();
- final int capacity = bodySize > maxBodySize ? maxBodySize : bodySize;
- java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocate(capacity);
+ int capacity = bodySize > maxBodySize ? maxBodySize : bodySize;
- int writtenSize = 0;
+ int writtenSize = capacity;
+ AMQBody firstContentBody = new MessageContentSourceBody(message,0,capacity);
- writtenSize += message.getContent(buf, writtenSize);
- buf.flip();
- AMQBody firstContentBody = PROTOCOL_CONVERTER.convertToBody(buf);
CompositeAMQBodyBlock
compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody);
@@ -137,15 +133,55 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter while(writtenSize < bodySize)
{
- buf = java.nio.ByteBuffer.allocate(capacity);
+ capacity = bodySize - writtenSize > maxBodySize ? maxBodySize : bodySize - writtenSize;
+ MessageContentSourceBody body = new MessageContentSourceBody(message, writtenSize, capacity);
+ writtenSize += capacity;
- writtenSize += message.getContent(buf, writtenSize);
- buf.flip();
- writeFrame(new AMQFrame(channelId, PROTOCOL_CONVERTER.convertToBody(buf)));
+ writeFrame(new AMQFrame(channelId, body));
}
}
}
+ private class MessageContentSourceBody implements AMQBody
+ {
+ public static final byte TYPE = 3;
+ private int _length;
+ private MessageContentSource _message;
+ private int _offset;
+
+ public MessageContentSourceBody(MessageContentSource message, int offset, int length)
+ {
+ _message = message;
+ _offset = offset;
+ _length = length;
+ }
+
+ public byte getFrameType()
+ {
+ return TYPE;
+ }
+
+ public int getSize()
+ {
+ return _length;
+ }
+
+ public void writePayload(DataOutputStream buffer) throws IOException
+ {
+ byte[] data = new byte[_length];
+
+ _message.getContent(ByteBuffer.wrap(data), _offset);
+
+ buffer.write(data);
+ }
+
+ public void handle(int channelId, AMQVersionAwareProtocolSession amqProtocolSession) throws AMQException
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+
private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody)
{
@@ -221,7 +257,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter return _underlyingBody.getSize();
}
- public void writePayload(ByteBuffer buffer)
+ public void writePayload(DataOutputStream buffer) throws IOException
{
if(_underlyingBody == null)
{
@@ -346,7 +382,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize();
}
- public void writePayload(ByteBuffer buffer)
+ public void writePayload(DataOutputStream buffer) throws IOException
{
AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody);
}
@@ -374,7 +410,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ;
}
- public void writePayload(ByteBuffer buffer)
+ public void writePayload(DataOutputStream buffer) throws IOException
{
AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9_1/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9_1/ProtocolOutputConverterImpl.java index cffbe445ee..10748298bc 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9_1/ProtocolOutputConverterImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9_1/ProtocolOutputConverterImpl.java @@ -20,9 +20,6 @@ package org.apache.qpid.server.output.amqp0_9_1; *
*/
-
-import org.apache.mina.common.ByteBuffer;
-
import org.apache.qpid.server.output.ProtocolOutputConverter;
import org.apache.qpid.server.output.HeaderPropertiesConverter;
import org.apache.qpid.server.protocol.AMQProtocolSession;
@@ -33,17 +30,16 @@ import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.framing.*;
import org.apache.qpid.framing.amqp_0_91.BasicGetBodyImpl;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
import org.apache.qpid.AMQException;
import org.apache.qpid.transport.DeliveryProperties;
import org.apache.qpid.protocol.AMQVersionAwareProtocolSession;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
{
private static final MethodRegistry METHOD_REGISTRY = MethodRegistry.getMethodRegistry(ProtocolVersion.v0_91);
- private static final ProtocolVersionMethodConverter
- PROTOCOL_CONVERTER = METHOD_REGISTRY.getProtocolVersionMethodConverter();
-
public static Factory getInstanceFactory()
{
@@ -121,15 +117,11 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter int maxBodySize = (int) getProtocolSession().getMaxFrameSize() - AMQFrame.getFrameOverhead();
- final int capacity = bodySize > maxBodySize ? maxBodySize : bodySize;
- java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocate(capacity);
+ int capacity = bodySize > maxBodySize ? maxBodySize : bodySize;
- int writtenSize = 0;
+ int writtenSize = capacity;
-
- writtenSize += message.getContent(buf, writtenSize);
- buf.flip();
- AMQBody firstContentBody = PROTOCOL_CONVERTER.convertToBody(buf);
+ AMQBody firstContentBody = new MessageContentSourceBody(message,0,capacity);
CompositeAMQBodyBlock
compositeBlock = new CompositeAMQBodyBlock(channelId, deliverBody, contentHeaderBody, firstContentBody);
@@ -137,15 +129,54 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter while(writtenSize < bodySize)
{
- buf = java.nio.ByteBuffer.allocate(capacity);
+ capacity = bodySize - writtenSize > maxBodySize ? maxBodySize : bodySize - writtenSize;
+ MessageContentSourceBody body = new MessageContentSourceBody(message, writtenSize, capacity);
+ writtenSize += capacity;
- writtenSize += message.getContent(buf, writtenSize);
- buf.flip();
- writeFrame(new AMQFrame(channelId, PROTOCOL_CONVERTER.convertToBody(buf)));
+ writeFrame(new AMQFrame(channelId, body));
}
}
}
+ private class MessageContentSourceBody implements AMQBody
+ {
+ public static final byte TYPE = 3;
+ private int _length;
+ private MessageContentSource _message;
+ private int _offset;
+
+ public MessageContentSourceBody(MessageContentSource message, int offset, int length)
+ {
+ _message = message;
+ _offset = offset;
+ _length = length;
+ }
+
+ public byte getFrameType()
+ {
+ return TYPE;
+ }
+
+ public int getSize()
+ {
+ return _length;
+ }
+
+ public void writePayload(DataOutputStream buffer) throws IOException
+ {
+ byte[] data = new byte[_length];
+
+ _message.getContent(java.nio.ByteBuffer.wrap(data), _offset);
+
+ buffer.write(data);
+ }
+
+ public void handle(int channelId, AMQVersionAwareProtocolSession amqProtocolSession) throws AMQException
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody)
{
@@ -221,7 +252,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter return _underlyingBody.getSize();
}
- public void writePayload(ByteBuffer buffer)
+ public void writePayload(DataOutputStream buffer) throws IOException
{
if(_underlyingBody == null)
{
@@ -346,7 +377,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() + _contentBody.getSize();
}
- public void writePayload(ByteBuffer buffer)
+ public void writePayload(DataOutputStream buffer) throws IOException
{
AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody, _contentBody);
}
@@ -374,7 +405,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter return OVERHEAD + _methodBody.getSize() + _headerBody.getSize() ;
}
- public void writePayload(ByteBuffer buffer)
+ public void writePayload(DataOutputStream buffer) throws IOException
{
AMQFrame.writeFrames(buffer, _channel, _methodBody, _headerBody);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtil.java b/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtil.java new file mode 100644 index 0000000000..644f714c8c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtil.java @@ -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. + */ +package org.apache.qpid.server.plugins; + +import java.util.Iterator; +import java.util.Map; + +import org.osgi.framework.Version; + +/** + * Utility class to convert a map of package name to version numbers into the string + * with the format expected of a OSGi system package declaration: + * + * <code> + * org.xyz; version=1.0.0, org.xyz.xyz; version=1.0.0,... + * </code> + * + * Additionally, if the caller has provided a qpidPackageReleaseNumber and the package + * begins org.apache.qpid, this release number will be used, in preference to the one + * found in the Map. + * + * @see org.osgi.framework.Constants#FRAMEWORK_SYSTEMPACKAGES + * + */ +public class OsgiSystemPackageUtil +{ + private static final String APACHE_QPID_PKG_PREFIX = "org.apache.qpid"; + + private final Map<String, String> _packageNameVersionMap; + private final Version _qpidPackageReleaseNumber; + + public OsgiSystemPackageUtil(final Version qpidPackageReleaseNumber, final Map<String, String> packageNameVersionMap) + { + _qpidPackageReleaseNumber = qpidPackageReleaseNumber; + _packageNameVersionMap = packageNameVersionMap; + } + + public String getFormattedSystemPackageString() + { + if (_packageNameVersionMap == null || _packageNameVersionMap.size() == 0) + { + return null; + } + + final StringBuilder packages = new StringBuilder(); + + for(Iterator<String> itr = _packageNameVersionMap.keySet().iterator(); itr.hasNext();) + { + final String packageName = itr.next(); + final String packageVersion; + + if (_qpidPackageReleaseNumber != null && packageName.startsWith(APACHE_QPID_PKG_PREFIX)) + { + packageVersion = _qpidPackageReleaseNumber.toString(); + } + else + { + packageVersion = _packageNameVersionMap.get(packageName); + } + + packages.append(packageName); + packages.append("; "); + packages.append("version="); + packages.append(packageVersion); + + if (itr.hasNext()) + { + packages.append(", "); + } + } + + return packages.toString(); + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties b/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties new file mode 100644 index 0000000000..aaab4f76cc --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/plugins/OsgiSystemPackages.properties @@ -0,0 +1,93 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one# +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# +# OSGi framework system package list +# +# PluginManager uses these properties to construct the FRAMEWORK_SYSTEMPACKAGES list +# + +# Format is: +# <package>=<version> +# and PluginManager will convert this into: +# <package>; version=<version> +# e.g. org.osgi.framework; version=1.3.0 + +javax.management.openmbean=1.0.0 +javax.management=1.0.0 + +javax.security.auth=1.0.0 +javax.security.auth.callback=1.0.0 +javax.security.sasl=1.0.0 +javax.security=1.0.0 + +org.xml.sax=1.0.0 +org.xml.sax.helpers=1.0.0 + +org.osgi.framework=1.3.0 +org.osgi.service.packageadmin=1.2.0 +org.osgi.service.startlevel=1.0.0 +org.osgi.service.url=1.0.0 +org.osgi.util.tracker=1.0.0 + +org.apache.commons.configuration=1.0.0 + +org.apache.commons.lang=1.0.0 +org.apache.commons.lang.builder=1.0.0 +org.apache.commons.logging=1.0.0 + +org.apache.log4j=1.2.12 + +org.slf4j=1.6.1 + +# For Qpid packages (org.apache.qpid), the version number is automatically overridden by QpidPropertis#getReleaseVersion() + +org.apache.qpid.junit.extensions.util=0.0.0 +org.apache.qpid=0.0.0 +org.apache.qpid.common=0.0.0 +org.apache.qpid.exchange=0.0.0 +org.apache.qpid.framing=0.0.0 +org.apache.qpid.management.common.mbeans.annotations=0.0.0 +org.apache.qpid.protocol=0.0.0 +org.apache.qpid.transport=0.0.0 +org.apache.qpid.transport.codec=0.0.0 +org.apache.qpid.server.binding=0.0.0 +org.apache.qpid.server.configuration=0.0.0 +org.apache.qpid.server.configuration.plugins=0.0.0 +org.apache.qpid.server.configuration.management=0.0.0 +org.apache.qpid.server.exchange=0.0.0 +org.apache.qpid.server.logging=0.0.0 +org.apache.qpid.server.logging.actors=0.0.0 +org.apache.qpid.server.logging.subjects=0.0.0 +org.apache.qpid.server.management=0.0.0 +org.apache.qpid.server.persistent=0.0.0 +org.apache.qpid.server.plugins=0.0.0 +org.apache.qpid.server.protocol=0.0.0 +org.apache.qpid.server.queue=0.0.0 +org.apache.qpid.server.registry=0.0.0 +org.apache.qpid.server.security=0.0.0 +org.apache.qpid.server.security.access=0.0.0 +org.apache.qpid.server.security.access.plugins=0.0.0 +org.apache.qpid.server.security.auth=0.0.0 +org.apache.qpid.server.security.auth.sasl=0.0.0 +org.apache.qpid.server.security.auth.manager=0.0.0 +org.apache.qpid.server.virtualhost=0.0.0 +org.apache.qpid.server.virtualhost.plugins=0.0.0 +org.apache.qpid.util=0.0.0 + diff --git a/java/broker/src/main/java/org/apache/qpid/server/plugins/Plugin.java b/java/broker/src/main/java/org/apache/qpid/server/plugins/Plugin.java index e7f9983fff..804a9d5027 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/plugins/Plugin.java +++ b/java/broker/src/main/java/org/apache/qpid/server/plugins/Plugin.java @@ -27,5 +27,5 @@ public interface Plugin /** * Provide Configuration to this plugin */ - public void configure(ConfigurationPlugin config); + public void configure(ConfigurationPlugin config) throws ConfigurationException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java b/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java index a6bab017a1..dab6c3b231 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java @@ -18,39 +18,56 @@ */ package org.apache.qpid.server.plugins; -import static org.apache.felix.framework.util.FelixConstants.*; -import static org.apache.felix.main.AutoProcessor.*; +import static org.apache.felix.framework.util.FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP; +import static org.apache.felix.main.AutoProcessor.AUTO_DEPLOY_ACTION_PROPERY; +import static org.apache.felix.main.AutoProcessor.AUTO_DEPLOY_DIR_PROPERY; +import static org.apache.felix.main.AutoProcessor.AUTO_DEPLOY_INSTALL_VALUE; +import static org.apache.felix.main.AutoProcessor.AUTO_DEPLOY_START_VALUE; +import static org.apache.felix.main.AutoProcessor.process; +import static org.osgi.framework.Constants.FRAMEWORK_STORAGE; +import static org.osgi.framework.Constants.FRAMEWORK_STORAGE_CLEAN; +import static org.osgi.framework.Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT; +import static org.osgi.framework.Constants.FRAMEWORK_SYSTEMPACKAGES; import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import org.apache.commons.configuration.ConfigurationException; import org.apache.felix.framework.Felix; import org.apache.felix.framework.util.StringMap; import org.apache.log4j.Logger; import org.apache.qpid.common.Closeable; +import org.apache.qpid.common.QpidProperties; import org.apache.qpid.server.configuration.TopicConfiguration; +import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionConfiguration.SlowConsumerDetectionConfigurationFactory; import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionPolicyConfiguration.SlowConsumerDetectionPolicyConfigurationFactory; import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionQueueConfiguration.SlowConsumerDetectionQueueConfigurationFactory; -import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; import org.apache.qpid.server.exchange.ExchangeType; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.SecurityPluginFactory; import org.apache.qpid.server.security.access.plugins.AllowAll; import org.apache.qpid.server.security.access.plugins.DenyAll; import org.apache.qpid.server.security.access.plugins.LegacyAccess; -import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory; +import org.apache.qpid.server.security.auth.manager.AuthenticationManagerPluginFactory; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; import org.apache.qpid.server.virtualhost.plugins.SlowConsumerDetection; +import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory; import org.apache.qpid.server.virtualhost.plugins.policies.TopicDeletePolicy; import org.apache.qpid.slowconsumerdetection.policies.SlowConsumerPolicyPluginFactory; +import org.apache.qpid.util.FileUtils; import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; +import org.osgi.framework.Version; import org.osgi.framework.launch.Framework; import org.osgi.util.tracker.ServiceTracker; @@ -63,7 +80,6 @@ public class PluginManager implements Closeable private static final Logger _logger = Logger.getLogger(PluginManager.class); private static final int FELIX_STOP_TIMEOUT = 30000; - private static final String QPID_VER_SUFFIX = "version=0.9,"; private Framework _felix; @@ -72,15 +88,61 @@ public class PluginManager implements Closeable private ServiceTracker _configTracker = null; private ServiceTracker _virtualHostTracker = null; private ServiceTracker _policyTracker = null; + private ServiceTracker _authenticationManagerTracker = null; private Activator _activator; + private final List<ServiceTracker> _trackers = new ArrayList<ServiceTracker>(); private Map<String, SecurityPluginFactory> _securityPlugins = new HashMap<String, SecurityPluginFactory>(); private Map<List<String>, ConfigurationPluginFactory> _configPlugins = new IdentityHashMap<List<String>, ConfigurationPluginFactory>(); private Map<String, VirtualHostPluginFactory> _vhostPlugins = new HashMap<String, VirtualHostPluginFactory>(); private Map<String, SlowConsumerPolicyPluginFactory> _policyPlugins = new HashMap<String, SlowConsumerPolicyPluginFactory>(); + private Map<String, AuthenticationManagerPluginFactory<? extends Plugin>> _authenticationManagerPlugins = new HashMap<String, AuthenticationManagerPluginFactory<? extends Plugin>>(); - public PluginManager(String pluginPath, String cachePath) throws Exception + /** The default name of the OSGI system package list. */ + private static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/server/plugins/OsgiSystemPackages.properties"; + + /** The name of the override system property that holds the name of the OSGI system package list. */ + private static final String FILE_PROPERTY = "qpid.osgisystempackages.properties"; + + private static final String OSGI_SYSTEM_PACKAGES; + + static + { + final String filename = System.getProperty(FILE_PROPERTY); + final InputStream is = FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME, + PluginManager.class.getClassLoader()); + + try + { + Version qpidReleaseVersion; + try + { + qpidReleaseVersion = Version.parseVersion(QpidProperties.getReleaseVersion()); + } + catch (IllegalArgumentException iae) + { + qpidReleaseVersion = null; + } + + final Properties p = new Properties(); + p.load(is); + + final OsgiSystemPackageUtil osgiSystemPackageUtil = new OsgiSystemPackageUtil(qpidReleaseVersion, (Map)p); + + OSGI_SYSTEM_PACKAGES = osgiSystemPackageUtil.getFormattedSystemPackageString(); + + _logger.debug("List of OSGi system packages to be added: " + OSGI_SYSTEM_PACKAGES); + } + catch (IOException e) + { + _logger.error("Error reading OSGI system package list", e); + throw new ExceptionInInitializerError(e); + } + } + + + public PluginManager(String pluginPath, String cachePath, BundleContext bundleContext) throws Exception { // Store all non-OSGi plugins // A little gross that we have to add them here, but not all the plugins are OSGIfied @@ -97,7 +159,8 @@ public class PluginManager implements Closeable LegacyAccess.LegacyAccessConfiguration.FACTORY, new SlowConsumerDetectionConfigurationFactory(), new SlowConsumerDetectionPolicyConfigurationFactory(), - new SlowConsumerDetectionQueueConfigurationFactory())) + new SlowConsumerDetectionQueueConfigurationFactory(), + PrincipalDatabaseAuthenticationManager.PrincipalDatabaseAuthenticationManagerConfiguration.FACTORY)) { _configPlugins.put(configFactory.getParentPaths(), configFactory); } @@ -112,125 +175,109 @@ public class PluginManager implements Closeable _vhostPlugins.put(pluginFactory.getClass().getName(), pluginFactory); } - // Check the plugin directory path is set and exist - if (pluginPath == null) + for (AuthenticationManagerPluginFactory<? extends Plugin> pluginFactory : Arrays.asList( + PrincipalDatabaseAuthenticationManager.FACTORY)) { - return; + _authenticationManagerPlugins.put(pluginFactory.getPluginName(), pluginFactory); } - File pluginDir = new File(pluginPath); - if (!pluginDir.exists()) - { - return; - } - - // Setup OSGi configuration propery map - StringMap configMap = new StringMap(false); - - // Add the bundle provided service interface package and the core OSGi - // packages to be exported from the class path via the system bundle. - configMap.put(FRAMEWORK_SYSTEMPACKAGES, - "org.osgi.framework; version=1.3.0," + - "org.osgi.service.packageadmin; version=1.2.0," + - "org.osgi.service.startlevel; version=1.0.0," + - "org.osgi.service.url; version=1.0.0," + - "org.osgi.util.tracker; version=1.0.0," + - "org.apache.qpid.junit.extensions.util; " + QPID_VER_SUFFIX + - "org.apache.qpid; " + QPID_VER_SUFFIX + - "org.apache.qpid.common; " + QPID_VER_SUFFIX + - "org.apache.qpid.exchange; " + QPID_VER_SUFFIX + - "org.apache.qpid.framing; " + QPID_VER_SUFFIX + - "org.apache.qpid.management.common.mbeans.annotations; " + QPID_VER_SUFFIX + - "org.apache.qpid.protocol; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.binding; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.configuration; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.configuration.plugins; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.configuration.management; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.exchange; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.logging; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.logging.actors; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.logging.subjects; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.management; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.persistent; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.plugins; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.protocol; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.queue; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.registry; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.security; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.security.access; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.security.access.plugins; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.virtualhost; " + QPID_VER_SUFFIX + - "org.apache.qpid.server.virtualhost.plugins; " + QPID_VER_SUFFIX + - "org.apache.qpid.util; " + QPID_VER_SUFFIX + - "org.apache.commons.configuration; version=1.0.0," + - "org.apache.commons.lang; version=1.0.0," + - "org.apache.commons.lang.builder; version=1.0.0," + - "org.apache.commons.logging; version=1.0.0," + - "org.apache.log4j; version=1.2.12," + - "javax.management.openmbean; version=1.0.0," + - "javax.management; version=1.0.0" - ); - - // No automatic shutdown hook - configMap.put("felix.shutdown.hook", "false"); - - // Add system activator - List<BundleActivator> activators = new ArrayList<BundleActivator>(); - _activator = new Activator(); - activators.add(_activator); - configMap.put(SYSTEMBUNDLE_ACTIVATORS_PROP, activators); - if (cachePath != null) + if(bundleContext == null) { - File cacheDir = new File(cachePath); - if (!cacheDir.exists() && cacheDir.canWrite()) + // Check the plugin directory path is set and exist + if (pluginPath == null) { - _logger.info("Creating plugin cache directory: " + cachePath); - cacheDir.mkdir(); + _logger.info("No plugin path specified, no plugins will be loaded."); + return; } - - // Set plugin cache directory and empty it - _logger.info("Cache bundles in directory " + cachePath); - configMap.put(FRAMEWORK_STORAGE, cachePath); - } - configMap.put(FRAMEWORK_STORAGE_CLEAN, FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT); - - // Set directory with plugins to auto-deploy - _logger.info("Auto deploying bundles from directory " + pluginPath); - configMap.put(AUTO_DEPLOY_DIR_PROPERY, pluginPath); - configMap.put(AUTO_DEPLOY_ACTION_PROPERY, AUTO_DEPLOY_INSTALL_VALUE + "," + AUTO_DEPLOY_START_VALUE); - - // Start plugin manager and trackers - _felix = new Felix(configMap); - try - { - _logger.info("Starting plugin manager..."); - _felix.init(); - process(configMap, _felix.getBundleContext()); - _felix.start(); - _logger.info("Started plugin manager"); + File pluginDir = new File(pluginPath); + if (!pluginDir.exists()) + { + _logger.warn("Plugin dir : " + pluginDir + " does not exist."); + return; + } + + // Add the bundle provided service interface package and the core OSGi + // packages to be exported from the class path via the system bundle. + + // Setup OSGi configuration property map + final StringMap configMap = new StringMap(false); + configMap.put(FRAMEWORK_SYSTEMPACKAGES, OSGI_SYSTEM_PACKAGES); + + // No automatic shutdown hook + configMap.put("felix.shutdown.hook", "false"); + + // Add system activator + List<BundleActivator> activators = new ArrayList<BundleActivator>(); + _activator = new Activator(); + activators.add(_activator); + configMap.put(SYSTEMBUNDLE_ACTIVATORS_PROP, activators); + + if (cachePath != null) + { + File cacheDir = new File(cachePath); + if (!cacheDir.exists() && cacheDir.canWrite()) + { + _logger.info("Creating plugin cache directory: " + cachePath); + cacheDir.mkdir(); + } + + // Set plugin cache directory and empty it + _logger.info("Cache bundles in directory " + cachePath); + configMap.put(FRAMEWORK_STORAGE, cachePath); + } + configMap.put(FRAMEWORK_STORAGE_CLEAN, FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT); + + // Set directory with plugins to auto-deploy + _logger.info("Auto deploying bundles from directory " + pluginPath); + configMap.put(AUTO_DEPLOY_DIR_PROPERY, pluginPath); + configMap.put(AUTO_DEPLOY_ACTION_PROPERY, AUTO_DEPLOY_INSTALL_VALUE + "," + AUTO_DEPLOY_START_VALUE); + + // Start plugin manager + _felix = new Felix(configMap); + try + { + _logger.info("Starting plugin manager framework"); + _felix.init(); + process(configMap, _felix.getBundleContext()); + _felix.start(); + _logger.info("Started plugin manager framework"); + } + catch (BundleException e) + { + throw new ConfigurationException("Could not start plugin manager: " + e.getMessage(), e); + } + + bundleContext = _activator.getContext(); } - catch (BundleException e) + else { - throw new ConfigurationException("Could not start plugin manager: " + e.getMessage(), e); + _logger.info("Using the specified external BundleContext"); } - - // TODO save trackers in a map, keyed by class name - - _exchangeTracker = new ServiceTracker(_activator.getContext(), ExchangeType.class.getName(), null); + + _exchangeTracker = new ServiceTracker(bundleContext, ExchangeType.class.getName(), null); _exchangeTracker.open(); + _trackers.add(_exchangeTracker); - _securityTracker = new ServiceTracker(_activator.getContext(), SecurityPluginFactory.class.getName(), null); + _securityTracker = new ServiceTracker(bundleContext, SecurityPluginFactory.class.getName(), null); _securityTracker.open(); + _trackers.add(_securityTracker); - _configTracker = new ServiceTracker(_activator.getContext(), ConfigurationPluginFactory.class.getName(), null); + _configTracker = new ServiceTracker(bundleContext, ConfigurationPluginFactory.class.getName(), null); _configTracker.open(); + _trackers.add(_configTracker); - _virtualHostTracker = new ServiceTracker(_activator.getContext(), VirtualHostPluginFactory.class.getName(), null); + _virtualHostTracker = new ServiceTracker(bundleContext, VirtualHostPluginFactory.class.getName(), null); _virtualHostTracker.open(); + _trackers.add(_virtualHostTracker); - _policyTracker = new ServiceTracker(_activator.getContext(), SlowConsumerPolicyPluginFactory.class.getName(), null); + _policyTracker = new ServiceTracker(bundleContext, SlowConsumerPolicyPluginFactory.class.getName(), null); _policyTracker.open(); - + _trackers.add(_policyTracker); + + _authenticationManagerTracker = new ServiceTracker(bundleContext, AuthenticationManagerPluginFactory.class.getName(), null); + _authenticationManagerTracker.open(); + _trackers.add(_authenticationManagerTracker); + _logger.info("Opened service trackers"); } @@ -301,22 +348,26 @@ public class PluginManager implements Closeable return getServices(_securityTracker, _securityPlugins); } + public Map<String, AuthenticationManagerPluginFactory<? extends Plugin>> getAuthenticationManagerPlugins() + { + return getServices(_authenticationManagerTracker, _authenticationManagerPlugins); + } + public void close() { - if (_felix != null) + try { - try + // Close all bundle trackers + for(ServiceTracker tracker : _trackers) { - // Close all bundle trackers - _exchangeTracker.close(); - _securityTracker.close(); - _configTracker.close(); - _virtualHostTracker.close(); - _policyTracker.close(); + tracker.close(); } - finally + } + finally + { + if (_felix != null) { - _logger.info("Stopping plugin manager"); + _logger.info("Stopping plugin manager framework"); try { // FIXME should be stopAndWait() but hangs VM, need upgrade in felix @@ -335,7 +386,12 @@ public class PluginManager implements Closeable { // Ignore } - _logger.info("Stopped plugin manager"); + _logger.info("Stopped plugin manager framework"); + } + else + { + _logger.info("Plugin manager was started with an external BundleContext, " + + "skipping remaining shutdown tasks"); } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java index bcda385f64..b51e6aff1a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java @@ -20,14 +20,35 @@ */ package org.apache.qpid.server.protocol; -import org.apache.qpid.protocol.AMQConstant; +import java.util.List; +import java.util.UUID; + import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.stats.StatisticsGatherer; -public interface AMQConnectionModel +public interface AMQConnectionModel extends StatisticsGatherer { + /** + * get a unique id for this connection. + * + * @return a {@link UUID} representing the connection + */ + public UUID getId(); + + /** + * Close the underlying Connection + * + * @param cause + * @param message + * @throws org.apache.qpid.AMQException + */ + public void close(AMQConstant cause, String message) throws AMQException; /** * Close the given requested Session + * * @param session * @param cause * @param message @@ -36,4 +57,20 @@ public interface AMQConnectionModel public void closeSession(AMQSessionModel session, AMQConstant cause, String message) throws AMQException; public long getConnectionId(); + + /** + * Get a list of all sessions using this connection. + * + * @return a list of {@link AMQSessionModel}s + */ + public List<AMQSessionModel> getSessionModels(); + + /** + * Return a {@link LogSubject} for the connection. + */ + public LogSubject getLogSubject(); + + public String getUserName(); + + public boolean isSessionNameUnique(String name); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index a1ffe272fd..bff0a79de1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -20,7 +20,9 @@ */ package org.apache.qpid.server.protocol; +import java.io.DataOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; @@ -30,18 +32,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; -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.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; import javax.management.JMException; +import javax.security.auth.Subject; 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; @@ -66,12 +66,10 @@ 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.Job; -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.protocol.ServerProtocolEngine; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.configuration.ConfigStore; import org.apache.qpid.server.configuration.ConfiguredObject; @@ -90,21 +88,22 @@ 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.security.auth.sasl.UsernamePrincipal; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.stats.StatisticsCounter; 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; +import org.apache.qpid.transport.TransportException; +import org.apache.qpid.transport.network.NetworkConnection; -public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocolSession, ConnectionConfig +public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQProtocolSession, ConnectionConfig { private static final Logger _logger = Logger.getLogger(AMQProtocolEngine.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; @@ -134,7 +133,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol private Object _lastSent; protected volatile boolean _closed; - + // maximum number of channels this session should have private long _maxNoOfChannels = ApplicationRegistry.getInstance().getConfiguration().getMaxChannelCount(); @@ -146,47 +145,46 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol private Map<Integer, Long> _closingChannelsList = new ConcurrentHashMap<Integer, Long>(); private ProtocolOutputConverter _protocolOutputConverter; - private Principal _authorizedID; + private Subject _authorizedSubject; private MethodDispatcher _dispatcher; private ProtocolSessionIdentifier _sessionIdentifier; - // Create a simple ID that increments for ever new Session - private final long _sessionID = idGenerator.getAndIncrement(); + private final long _sessionID; 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(); private long _maxFrameSize; private final AtomicBoolean _closing = new AtomicBoolean(false); private final UUID _id; private final ConfigStore _configStore; private long _createTime = System.currentTimeMillis(); + private ApplicationRegistry _registry; + private boolean _statisticsEnabled = false; + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; + + private NetworkConnection _network; + private Sender<ByteBuffer> _sender; + public ManagedObject getManagedObject() { return _managedObject; } - public AMQProtocolEngine(VirtualHostRegistry virtualHostRegistry, NetworkDriver driver) + public AMQProtocolEngine(VirtualHostRegistry virtualHostRegistry, NetworkConnection network, final long connectionId) { _stateManager = new AMQStateManager(virtualHostRegistry, this); - _networkDriver = driver; - _codecFactory = new AMQCodecFactory(true, this); - _poolReference.acquireExecutorService(); - _readJob = new Job(_poolReference, Job.MAX_JOB_EVENTS, true); - _writeJob = new Job(_poolReference, Job.MAX_JOB_EVENTS, false); + + setNetworkConnection(network); + _sessionID = connectionId; _actor = new AMQPConnectionActor(this, virtualHostRegistry.getApplicationRegistry().getRootMessageLogger()); @@ -195,9 +193,21 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol _configStore = virtualHostRegistry.getConfigStore(); _id = _configStore.createId(); - _actor.message(ConnectionMessages.OPEN(null, null, false, false)); + _registry = virtualHostRegistry.getApplicationRegistry(); + initialiseStatistics(); + } + + public void setNetworkConnection(NetworkConnection network) + { + setNetworkConnection(network, network.getSender()); + } + + public void setNetworkConnection(NetworkConnection network, Sender<ByteBuffer> sender) + { + _network = network; + _sender = sender; } private AMQProtocolSessionMBean createMBean() throws JMException @@ -236,26 +246,18 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol try { final ArrayList<AMQDataBlock> dataBlocks = _codecFactory.getDecoder().decodeBuffer(msg); - Job.fireAsynchEvent(_poolReference.getPool(), _readJob, new Runnable() + for (AMQDataBlock dataBlock : dataBlocks) { - public void run() + try { - // Decode buffer - - for (AMQDataBlock dataBlock : dataBlocks) - { - try - { - dataBlockReceived(dataBlock); - } - catch (Exception e) - { - _logger.error("Unexpected exception when processing datablock", e); - closeProtocolSession(); - } - } + dataBlockReceived(dataBlock); } - }); + catch (Exception e) + { + _logger.error("Unexpected exception when processing datablock", e); + closeProtocolSession(); + } + } } catch (Exception e) { @@ -333,6 +335,11 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol closeChannel(channelId); throw e; } + catch (TransportException e) + { + closeChannel(channelId); + throw e; + } } finally { @@ -343,7 +350,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol private void protocolInitiationReceived(ProtocolInitiation pi) { // this ensures the codec never checks for a PI message again - ((AMQDecoder) _codecFactory.getDecoder()).setExpectProtocolInitiation(false); + (_codecFactory.getDecoder()).setExpectProtocolInitiation(false); try { // Log incomming protocol negotiation request @@ -363,15 +370,49 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol null, mechanisms.getBytes(), locales.getBytes()); - _networkDriver.send(responseBody.generateFrame(0).toNioByteBuffer()); + _sender.send(asByteBuffer(responseBody.generateFrame(0))); + _sender.flush(); } catch (AMQException e) { _logger.info("Received unsupported protocol initiation for protocol version: " + getProtocolVersion()); - _networkDriver.send(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion()).toNioByteBuffer()); + _sender.send(asByteBuffer(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion()))); + _sender.flush(); + } + } + + private ByteBuffer asByteBuffer(AMQDataBlock block) + { + final ByteBuffer buf = ByteBuffer.allocate((int) block.getSize()); + + try + { + block.writePayload(new DataOutputStream(new OutputStream() + { + + + @Override + public void write(int b) throws IOException + { + buf.put((byte) b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException + { + buf.put(b, off, len); + } + })); + } + catch (IOException e) + { + throw new RuntimeException(e); } + + buf.flip(); + return buf; } public void methodFrameReceived(int channelId, AMQMethodBody methodBody) @@ -426,19 +467,19 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol AMQConstant.CHANNEL_ERROR.getName().toString()); _logger.info(e.getMessage() + " whilst processing:" + methodBody); - closeConnection(channelId, ce, false); + closeConnection(channelId, ce); } } catch (AMQConnectionException e) { _logger.info(e.getMessage() + " whilst processing:" + methodBody); - closeConnection(channelId, e, false); + closeConnection(channelId, e); } catch (AMQSecurityException e) { AMQConnectionException ce = evt.getMethod().getConnectionException(AMQConstant.ACCESS_REFUSED, e.getMessage()); _logger.info(e.getMessage() + " whilst processing:" + methodBody); - closeConnection(channelId, ce, false); + closeConnection(channelId, ce); } } catch (Exception e) @@ -481,19 +522,14 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol * * @param frame the frame to write */ - public void writeFrame(AMQDataBlock frame) + public synchronized void writeFrame(AMQDataBlock frame) { _lastSent = frame; - final ByteBuffer buf = frame.toNioByteBuffer(); + final ByteBuffer buf = asByteBuffer(frame); _lastIoTime = System.currentTimeMillis(); _writtenBytes += buf.remaining(); - Job.fireAsynchEvent(_poolReference.getPool(), _writeJob, new Runnable() - { - public void run() - { - _networkDriver.send(buf); - } - }); + _sender.send(buf); + _sender.flush(); } public AMQShortString getContextKey() @@ -683,8 +719,8 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol { if (delay > 0) { - _networkDriver.setMaxWriteIdle(delay); - _networkDriver.setMaxReadIdle((int) (ApplicationRegistry.getInstance().getConfiguration().getHeartBeatTimeout() * delay)); + _network.setMaxWriteIdle(delay); + _network.setMaxReadIdle((int) (ApplicationRegistry.getInstance().getConfiguration().getHeartBeatTimeout() * delay)); } } @@ -725,7 +761,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol } closeAllChannels(); - + getConfigStore().removeConfiguredObject(this); if (_managedObject != null) @@ -745,7 +781,6 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol _closed = true; notifyAll(); } - _poolReference.releaseExecutorService(); CurrentActor.get().message(_logSubject, ConnectionMessages.CLOSE()); } } @@ -768,27 +803,32 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol } } - public void closeConnection(int channelId, AMQConnectionException e, boolean closeProtocolSession) throws AMQException + public void closeConnection(int channelId, AMQConnectionException e) throws AMQException { - if (_logger.isInfoEnabled()) + try { - _logger.info("Closing connection due to: " + e); - } - - markChannelAwaitingCloseOk(channelId); - closeSession(); - _stateManager.changeState(AMQState.CONNECTION_CLOSING); - writeFrame(e.getCloseFrame(channelId)); + if (_logger.isInfoEnabled()) + { + _logger.info("Closing connection due to: " + e); + } - if (closeProtocolSession) + markChannelAwaitingCloseOk(channelId); + closeSession(); + _stateManager.changeState(AMQState.CONNECTION_CLOSING); + writeFrame(e.getCloseFrame(channelId)); + } + finally { closeProtocolSession(); } + + } public void closeProtocolSession() { - _networkDriver.close(); + _network.close(); + try { _stateManager.changeState(AMQState.CONNECTION_CLOSED); @@ -797,11 +837,15 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol { _logger.info(e.getMessage()); } + catch (TransportException e) + { + _logger.info(e.getMessage()); + } } public String toString() { - return getRemoteAddress() + "(" + (getAuthorizedID() == null ? "?" : getAuthorizedID().getName() + ")"); + return getRemoteAddress() + "(" + (getAuthorizedPrincipal() == null ? "?" : getAuthorizedPrincipal().getName() + ")"); } public String dump() @@ -823,17 +867,11 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol */ 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. + SocketAddress address = _network.getLocalAddress(); 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); @@ -912,7 +950,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol public Object getClientIdentifier() { - return (_networkDriver != null) ? _networkDriver.getRemoteAddress() : null; + return _network.getRemoteAddress(); } public VirtualHost getVirtualHost() @@ -925,7 +963,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol _virtualHost = virtualHost; _virtualHost.getConnectionRegistry().registerConnection(this); - + _configStore.addConfiguredObject(this); try @@ -954,29 +992,33 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol return _protocolOutputConverter; } - public void setAuthorizedID(Principal authorizedID) + public void setAuthorizedSubject(final Subject authorizedSubject) { - _authorizedID = authorizedID; + if (authorizedSubject == null) + { + throw new IllegalArgumentException("authorizedSubject cannot be null"); + } + _authorizedSubject = authorizedSubject; } - public Principal getAuthorizedID() + public Subject getAuthorizedSubject() { - return _authorizedID; + return _authorizedSubject; } - public Principal getPrincipal() + public Principal getAuthorizedPrincipal() { - return _authorizedID; + return _authorizedSubject == null ? null : UsernamePrincipal.getUsernamePrincipalFromSubject(_authorizedSubject); } public SocketAddress getRemoteAddress() { - return _networkDriver.getRemoteAddress(); + return _network.getRemoteAddress(); } public SocketAddress getLocalAddress() { - return _networkDriver.getLocalAddress(); + return _network.getLocalAddress(); } public MethodRegistry getMethodRegistry() @@ -999,6 +1041,10 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol { _logger.error("Could not close protocol engine", e); } + catch (TransportException e) + { + _logger.error("Could not close protocol engine", e); + } } public void readerIdle() @@ -1006,14 +1052,9 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol // Nothing } - public void setNetworkDriver(NetworkDriver driver) - { - _networkDriver = driver; - } - public void writerIdle() { - _networkDriver.send(HeartbeatBody.FRAME.toNioByteBuffer()); + _sender.send(asByteBuffer(HeartbeatBody.FRAME)); } public void exception(Throwable throwable) @@ -1021,7 +1062,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol if (throwable instanceof AMQProtocolHeaderException) { writeFrame(new ProtocolInitiation(ProtocolVersion.getLatestSupportedVersion())); - _networkDriver.close(); + _sender.close(); _logger.error("Error in protocol initiation " + this + ":" + getRemoteAddress() + " :" + throwable.getMessage(), throwable); } @@ -1039,7 +1080,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol writeFrame(closeBody.generateFrame(0)); - _networkDriver.close(); + _sender.close(); } } @@ -1078,19 +1119,6 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol return (_clientVersion == null) ? null : _clientVersion.toString(); } - public void closeIfLingeringClosedChannels() - { - for (Entry<Integer, Long>id : _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(); - } - } - } - public Boolean isIncoming() { return true; @@ -1108,7 +1136,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol public String getAuthId() { - return getAuthorizedID().getName(); + return getAuthorizedPrincipal().getName(); } public Integer getRemotePID() @@ -1170,7 +1198,7 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol { return false; } - + public void mgmtClose() { MethodRegistry methodRegistry = getMethodRegistry(); @@ -1263,7 +1291,6 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol public void closeSession(AMQSessionModel session, AMQConstant cause, String message) throws AMQException { - closeChannel((Integer)session.getID()); MethodRegistry methodRegistry = getMethodRegistry(); @@ -1273,6 +1300,110 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol new AMQShortString(message), 0,0); - writeFrame(responseBody.generateFrame((Integer)session.getID())); - } + writeFrame(responseBody.generateFrame((Integer)session.getID())); + } + + public void close(AMQConstant cause, String message) throws AMQException + { + closeConnection(0, new AMQConnectionException(cause, message, 0, 0, + getProtocolOutputConverter().getProtocolMajorVersion(), + getProtocolOutputConverter().getProtocolMinorVersion(), + (Throwable) null)); + } + + public List<AMQSessionModel> getSessionModels() + { + List<AMQSessionModel> sessions = new ArrayList<AMQSessionModel>(); + for (AMQChannel channel : getChannels()) + { + sessions.add((AMQSessionModel) channel); + } + return sessions; + } + + public LogSubject getLogSubject() + { + return _logSubject; + } + + public void registerMessageDelivered(long messageSize) + { + if (isStatisticsEnabled()) + { + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); + } + _virtualHost.registerMessageDelivered(messageSize); + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + if (isStatisticsEnabled()) + { + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); + } + _virtualHost.registerMessageReceived(messageSize, timestamp); + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return _messagesReceived; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return _dataReceived; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return _messagesDelivered; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return _dataDelivered; + } + + public void resetStatistics() + { + _messagesDelivered.reset(); + _dataDelivered.reset(); + _messagesReceived.reset(); + _dataReceived.reset(); + } + + public void initialiseStatistics() + { + setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && + _registry.getConfiguration().isStatisticsGenerationConnectionsEnabled()); + + _messagesDelivered = new StatisticsCounter("messages-delivered-" + getSessionID()); + _dataDelivered = new StatisticsCounter("data-delivered-" + getSessionID()); + _messagesReceived = new StatisticsCounter("messages-received-" + getSessionID()); + _dataReceived = new StatisticsCounter("data-received-" + getSessionID()); + } + + public boolean isStatisticsEnabled() + { + return _statisticsEnabled; + } + + public void setStatisticsEnabled(boolean enabled) + { + _statisticsEnabled = enabled; + } + + @Override + public boolean isSessionNameUnique(String name) + { + return true; + } + + @Override + public String getUserName() + { + return getAuthorizedPrincipal().getName(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngineFactory.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngineFactory.java deleted file mode 100644 index 0e4444725e..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngineFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.apache.qpid.server.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 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/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java index f48a214933..c1b5b02f8f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.protocol; +import javax.security.auth.Subject; import javax.security.sasl.SaslServer; import org.apache.qpid.AMQException; @@ -28,16 +29,15 @@ import org.apache.qpid.framing.*; import org.apache.qpid.AMQConnectionException; import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.security.PrincipalHolder; +import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.logging.LogActor; 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, PrincipalHolder, AMQConnectionModel +public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, AuthorizationHolder, AMQConnectionModel { long getSessionID(); @@ -163,8 +163,10 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Prin /** This must be called when the session is _closed in order to free up any resources managed by the session. */ void closeSession() throws AMQException; + void closeProtocolSession(); + /** This must be called to close the session in order to free up any resources managed by the session. */ - void closeConnection(int channelId, AMQConnectionException e, boolean closeProtocolSession) throws AMQException; + void closeConnection(int channelId, AMQConnectionException e) throws AMQException; /** @return a key that uniquely identifies this session */ @@ -205,7 +207,7 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Prin public ProtocolOutputConverter getProtocolOutputConverter(); - void setAuthorizedID(Principal authorizedID); + void setAuthorizedSubject(Subject authorizedSubject); public java.net.SocketAddress getRemoteAddress(); @@ -231,7 +233,5 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Prin List<AMQChannel> getChannels(); - void closeIfLingeringClosedChannels(); - void mgmtCloseChannel(int channelId); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java index f4f2cab2c2..16d99de492 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java @@ -37,25 +37,15 @@ */ package org.apache.qpid.server.protocol; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ConnectionCloseBody; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.management.common.mbeans.ManagedConnection; -import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; -import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.ManagementActor; -import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.management.ManagedObject; +import java.util.Date; +import java.util.List; import javax.management.JMException; import javax.management.MBeanException; import javax.management.MBeanNotificationInfo; import javax.management.NotCompliantMBeanException; import javax.management.Notification; +import javax.management.ObjectName; import javax.management.monitor.MonitorNotification; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; @@ -66,8 +56,20 @@ import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; -import java.util.Date; -import java.util.List; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.management.common.mbeans.ManagedConnection; +import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; +import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.ManagementActor; +import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.management.ManagedObject; /** * This MBean class implements the management interface. In order to make more attributes, operations and notifications @@ -94,8 +96,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed super(ManagedConnection.class, ManagedConnection.TYPE); _protocolSession = amqProtocolSession; String remote = getRemoteAddress(); - remote = "anonymous".equals(remote) ? (remote + hashCode()) : remote; - _name = jmxEncode(new StringBuffer(remote), 0).toString(); + _name = "anonymous".equals(remote) ? (remote + hashCode()) : remote; init(); } @@ -130,7 +131,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed public String getAuthorizedId() { - return (_protocolSession.getPrincipal() != null ) ? _protocolSession.getPrincipal().getName() : null; + return (_protocolSession.getAuthorizedPrincipal() != null ) ? _protocolSession.getAuthorizedPrincipal().getName() : null; } public String getVersion() @@ -175,7 +176,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed public String getObjectInstanceName() { - return _name; + return ObjectName.quote(_name); } /** @@ -339,4 +340,78 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed _broadcaster.sendNotification(n); } -} // End of MBean class + public void resetStatistics() throws Exception + { + _protocolSession.resetStatistics(); + } + + public double getPeakMessageDeliveryRate() + { + return _protocolSession.getMessageDeliveryStatistics().getPeak(); + } + + public double getPeakDataDeliveryRate() + { + return _protocolSession.getDataDeliveryStatistics().getPeak(); + } + + public double getMessageDeliveryRate() + { + return _protocolSession.getMessageDeliveryStatistics().getRate(); + } + + public double getDataDeliveryRate() + { + return _protocolSession.getDataDeliveryStatistics().getRate(); + } + + public long getTotalMessagesDelivered() + { + return _protocolSession.getMessageDeliveryStatistics().getTotal(); + } + + public long getTotalDataDelivered() + { + return _protocolSession.getDataDeliveryStatistics().getTotal(); + } + + public double getPeakMessageReceiptRate() + { + return _protocolSession.getMessageReceiptStatistics().getPeak(); + } + + public double getPeakDataReceiptRate() + { + return _protocolSession.getDataReceiptStatistics().getPeak(); + } + + public double getMessageReceiptRate() + { + return _protocolSession.getMessageReceiptStatistics().getRate(); + } + + public double getDataReceiptRate() + { + return _protocolSession.getDataReceiptStatistics().getRate(); + } + + public long getTotalMessagesReceived() + { + return _protocolSession.getMessageReceiptStatistics().getTotal(); + } + + public long getTotalDataReceived() + { + return _protocolSession.getDataReceiptStatistics().getTotal(); + } + + public boolean isStatisticsEnabled() + { + return _protocolSession.isStatisticsEnabled(); + } + + public void setStatisticsEnabled(boolean enabled) + { + _protocolSession.setStatisticsEnabled(enabled); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java index a9b2354d75..bc63403a86 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java @@ -20,15 +20,35 @@ */ package org.apache.qpid.server.protocol; +import org.apache.qpid.AMQException; import org.apache.qpid.server.logging.LogSubject; public interface AMQSessionModel { - Object getID(); + public Object getID(); - AMQConnectionModel getConnectionModel(); + public AMQConnectionModel getConnectionModel(); - String getClientID(); + public String getClientID(); + + public void close() throws AMQException; - LogSubject getLogSubject(); + public LogSubject getLogSubject(); + + /** + * This method is called from the housekeeping thread to check the status of + * transactions on this session and react appropriately. + * + * If a transaction is open for too long or idle for too long then a warning + * is logged or the connection is closed, depending on the configuration. An open + * transaction is one that has recent activity. The transaction age is counted + * from the time the transaction was started. An idle transaction is one that + * has had no activity, such as publishing or acknowledgeing messages. + * + * @param openWarn time in milliseconds before alerting on open transaction + * @param openClose time in milliseconds before closing connection with open transaction + * @param idleWarn time in milliseconds before alerting on idle transaction + * @param idleClose time in milliseconds before closing connection with idle transaction + */ + public void checkTransactionStatus(long openWarn, long openClose, long idleWarn, long idleClose) throws AMQException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/PrincipalHolder.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java index 7e93623cab..e925d7a1ec 100755..100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/PrincipalHolder.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java @@ -18,12 +18,6 @@ * under the License. * */ -package org.apache.qpid.server.security; +package org.apache.qpid.server.protocol; -import java.security.Principal; - -public interface PrincipalHolder -{ - /** @return a Principal that was used to authorized this session */ - Principal getPrincipal(); -} +public enum AmqpProtocolVersion { v0_8, v0_9, v0_9_1, v0_10 }
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java index eb957ee33c..7033bf755d 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java @@ -22,45 +22,54 @@ package org.apache.qpid.server.protocol; import org.apache.log4j.Logger; -import org.apache.qpid.protocol.ProtocolEngine; -import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory.VERSION; +import org.apache.qpid.protocol.ServerProtocolEngine; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.transport.ServerConnection; import org.apache.qpid.transport.ConnectionDelegate; -import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.network.NetworkConnection; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.util.Set; -public class MultiVersionProtocolEngine implements ProtocolEngine +public class MultiVersionProtocolEngine implements ServerProtocolEngine { private static final Logger _logger = Logger.getLogger(MultiVersionProtocolEngine.class); + private final long _id; - - private NetworkDriver _networkDriver; - private Set<VERSION> _supported; + private Set<AmqpProtocolVersion> _supported; private String _fqdn; private IApplicationRegistry _appRegistry; + private NetworkConnection _network; + private Sender<ByteBuffer> _sender; + + private volatile ServerProtocolEngine _delegate = new SelfDelegateProtocolEngine(); - private volatile ProtocolEngine _delegate = new SelfDelegateProtocolEngine(); + public MultiVersionProtocolEngine(IApplicationRegistry appRegistry, + String fqdn, + Set<AmqpProtocolVersion> supported, + NetworkConnection network, + long id) + { + this(appRegistry,fqdn,supported,id); + setNetworkConnection(network); + } public MultiVersionProtocolEngine(IApplicationRegistry appRegistry, String fqdn, - Set<VERSION> supported, NetworkDriver networkDriver) + Set<AmqpProtocolVersion> supported, + long id) { + _id = id; _appRegistry = appRegistry; _fqdn = fqdn; _supported = supported; - _networkDriver = networkDriver; - } - public void setNetworkDriver(NetworkDriver driver) - { - _delegate.setNetworkDriver(driver); } + public SocketAddress getRemoteAddress() { return _delegate.getRemoteAddress(); @@ -96,6 +105,7 @@ public class MultiVersionProtocolEngine implements ProtocolEngine _delegate.readerIdle(); } + public void received(ByteBuffer msg) { _delegate.received(msg); @@ -106,6 +116,11 @@ public class MultiVersionProtocolEngine implements ProtocolEngine _delegate.exception(t); } + public long getConnectionId() + { + return _delegate.getConnectionId(); + } + private static final int MINIMUM_REQUIRED_HEADER_BYTES = 8; private static final byte[] AMQP_0_8_HEADER = @@ -130,7 +145,7 @@ public class MultiVersionProtocolEngine implements ProtocolEngine (byte) 9 }; -private static final byte[] AMQP_0_9_1_HEADER = + private static final byte[] AMQP_0_9_1_HEADER = new byte[] { (byte) 'A', (byte) 'M', (byte) 'Q', @@ -153,19 +168,31 @@ private static final byte[] AMQP_0_9_1_HEADER = (byte) 10 }; + public void setNetworkConnection(NetworkConnection networkConnection) + { + setNetworkConnection(networkConnection, networkConnection.getSender()); + } + + public void setNetworkConnection(NetworkConnection network, Sender<ByteBuffer> sender) + { + _network = network; + _sender = sender; + } + + private static interface DelegateCreator { - VERSION getVersion(); + AmqpProtocolVersion getVersion(); byte[] getHeaderIdentifier(); - ProtocolEngine getProtocolEngine(); + ServerProtocolEngine getProtocolEngine(); } private DelegateCreator creator_0_8 = new DelegateCreator() { - public VERSION getVersion() + public AmqpProtocolVersion getVersion() { - return VERSION.v0_8; + return AmqpProtocolVersion.v0_8; } public byte[] getHeaderIdentifier() @@ -173,18 +200,18 @@ private static final byte[] AMQP_0_9_1_HEADER = return AMQP_0_8_HEADER; } - public ProtocolEngine getProtocolEngine() + public ServerProtocolEngine getProtocolEngine() { - return new AMQProtocolEngine(_appRegistry.getVirtualHostRegistry(), _networkDriver); + return new AMQProtocolEngine(_appRegistry.getVirtualHostRegistry(), _network, _id); } }; private DelegateCreator creator_0_9 = new DelegateCreator() { - public VERSION getVersion() + public AmqpProtocolVersion getVersion() { - return VERSION.v0_9; + return AmqpProtocolVersion.v0_9; } @@ -193,18 +220,18 @@ private static final byte[] AMQP_0_9_1_HEADER = return AMQP_0_9_HEADER; } - public ProtocolEngine getProtocolEngine() + public ServerProtocolEngine getProtocolEngine() { - return new AMQProtocolEngine(_appRegistry.getVirtualHostRegistry(), _networkDriver); + return new AMQProtocolEngine(_appRegistry.getVirtualHostRegistry(), _network, _id); } }; private DelegateCreator creator_0_9_1 = new DelegateCreator() { - public VERSION getVersion() + public AmqpProtocolVersion getVersion() { - return VERSION.v0_9_1; + return AmqpProtocolVersion.v0_9_1; } @@ -213,9 +240,9 @@ private static final byte[] AMQP_0_9_1_HEADER = return AMQP_0_9_1_HEADER; } - public ProtocolEngine getProtocolEngine() + public ServerProtocolEngine getProtocolEngine() { - return new AMQProtocolEngine(_appRegistry.getVirtualHostRegistry(), _networkDriver); + return new AMQProtocolEngine(_appRegistry.getVirtualHostRegistry(), _network, _id); } }; @@ -223,9 +250,9 @@ private static final byte[] AMQP_0_9_1_HEADER = private DelegateCreator creator_0_10 = new DelegateCreator() { - public VERSION getVersion() + public AmqpProtocolVersion getVersion() { - return VERSION.v0_10; + return AmqpProtocolVersion.v0_10; } @@ -234,15 +261,15 @@ private static final byte[] AMQP_0_9_1_HEADER = return AMQP_0_10_HEADER; } - public ProtocolEngine getProtocolEngine() + public ServerProtocolEngine getProtocolEngine() { final ConnectionDelegate connDelegate = new org.apache.qpid.server.transport.ServerConnectionDelegate(_appRegistry, _fqdn); - ServerConnection conn = new ServerConnection(); + ServerConnection conn = new ServerConnection(_id); conn.setConnectionDelegate(connDelegate); - return new ProtocolEngine_0_10( conn, _networkDriver, _appRegistry); + return new ProtocolEngine_0_10( conn, _network, _appRegistry); } }; @@ -250,21 +277,16 @@ private static final byte[] AMQP_0_9_1_HEADER = new DelegateCreator[] { creator_0_8, creator_0_9, creator_0_9_1, creator_0_10 }; - private class ClosedDelegateProtocolEngine implements ProtocolEngine + private class ClosedDelegateProtocolEngine implements ServerProtocolEngine { - public void setNetworkDriver(NetworkDriver driver) - { - _networkDriver = driver; - } - public SocketAddress getRemoteAddress() { - return _networkDriver.getRemoteAddress(); + return _network.getRemoteAddress(); } public SocketAddress getLocalAddress() { - return _networkDriver.getLocalAddress(); + return _network.getLocalAddress(); } public long getWrittenBytes() @@ -301,26 +323,30 @@ private static final byte[] AMQP_0_9_1_HEADER = { } - } - private class SelfDelegateProtocolEngine implements ProtocolEngine - { + public void setNetworkConnection(NetworkConnection network, Sender<ByteBuffer> sender) + { - private final ByteBuffer _header = ByteBuffer.allocate(MINIMUM_REQUIRED_HEADER_BYTES); + } - public void setNetworkDriver(NetworkDriver driver) + public long getConnectionId() { - _networkDriver = driver; + return _id; } + } + + private class SelfDelegateProtocolEngine implements ServerProtocolEngine + { + private final ByteBuffer _header = ByteBuffer.allocate(MINIMUM_REQUIRED_HEADER_BYTES); public SocketAddress getRemoteAddress() { - return _networkDriver.getRemoteAddress(); + return _network.getRemoteAddress(); } public SocketAddress getLocalAddress() { - return _networkDriver.getLocalAddress(); + return _network.getLocalAddress(); } public long getWrittenBytes() @@ -355,7 +381,7 @@ private static final byte[] AMQP_0_9_1_HEADER = _header.get(headerBytes); - ProtocolEngine newDelegate = null; + ServerProtocolEngine newDelegate = null; byte[] newestSupported = null; for(int i = 0; newDelegate == null && i < _creators.length; i++) @@ -380,17 +406,20 @@ private static final byte[] AMQP_0_9_1_HEADER = // If no delegate is found then send back the most recent support protocol version id if(newDelegate == null) { - _networkDriver.send(ByteBuffer.wrap(newestSupported)); + _sender.send(ByteBuffer.wrap(newestSupported)); + _sender.flush(); _delegate = new ClosedDelegateProtocolEngine(); + + _network.close(); + } else { - newDelegate.setNetworkDriver(_networkDriver); - _delegate = newDelegate; _header.flip(); + _delegate.setNetworkConnection(_network, _sender); _delegate.received(_header); if(msg.hasRemaining()) { @@ -402,6 +431,11 @@ private static final byte[] AMQP_0_9_1_HEADER = } + public long getConnectionId() + { + return _id; + } + public void exception(Throwable t) { _logger.error("Error establishing session", t); @@ -421,5 +455,10 @@ private static final byte[] AMQP_0_9_1_HEADER = { } + + public void setNetworkConnection(NetworkConnection network, Sender<ByteBuffer> sender) + { + + } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactory.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactory.java index 75358c42d9..7e327b221f 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactory.java @@ -20,56 +20,38 @@ */ package org.apache.qpid.server.protocol; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; + import org.apache.qpid.protocol.ProtocolEngineFactory; -import org.apache.qpid.protocol.ProtocolEngine; -import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.protocol.ServerProtocolEngine; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; - -import java.util.Set; -import java.util.Arrays; -import java.util.HashSet; +import org.apache.qpid.transport.network.NetworkConnection; public class MultiVersionProtocolEngineFactory implements ProtocolEngineFactory { - ; - - - public enum VERSION { v0_8, v0_9, v0_9_1, v0_10 }; - - private static final Set<VERSION> ALL_VERSIONS = new HashSet<VERSION>(Arrays.asList(VERSION.values())); + private static final AtomicLong ID_GENERATOR = new AtomicLong(0); private final IApplicationRegistry _appRegistry; private final String _fqdn; - private final Set<VERSION> _supported; - + private final Set<AmqpProtocolVersion> _supported; - public MultiVersionProtocolEngineFactory() + public MultiVersionProtocolEngineFactory(String fqdn, Set<AmqpProtocolVersion> supportedVersions) { - this(1, "localhost", ALL_VERSIONS); - } - - public MultiVersionProtocolEngineFactory(String fqdn, Set<VERSION> versions) - { - this(1, fqdn, versions); + _appRegistry = ApplicationRegistry.getInstance(); + _fqdn = fqdn; + _supported = supportedVersions; } - - public MultiVersionProtocolEngineFactory(String fqdn) + public ServerProtocolEngine newProtocolEngine(NetworkConnection network) { - this(1, fqdn, ALL_VERSIONS); + return new MultiVersionProtocolEngine(_appRegistry, _fqdn, _supported, network, ID_GENERATOR.getAndIncrement()); } - public MultiVersionProtocolEngineFactory(int instance, String fqdn, Set<VERSION> supportedVersions) + public ServerProtocolEngine newProtocolEngine() { - _appRegistry = ApplicationRegistry.getInstance(instance); - _fqdn = fqdn; - _supported = supportedVersions; + return new MultiVersionProtocolEngine(_appRegistry, _fqdn, _supported, ID_GENERATOR.getAndIncrement()); } - - public ProtocolEngine newProtocolEngine(NetworkDriver networkDriver) - { - return new MultiVersionProtocolEngine(_appRegistry, _fqdn, _supported, networkDriver); - } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java index 30d506a89b..48a8a1bf42 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_0_10.java @@ -20,25 +20,26 @@ */ package org.apache.qpid.server.protocol; -import org.apache.qpid.protocol.ProtocolEngine; -import org.apache.qpid.transport.NetworkDriver; +import org.apache.qpid.protocol.ServerProtocolEngine; +import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.network.InputHandler; import org.apache.qpid.transport.network.Assembler; import org.apache.qpid.transport.network.Disassembler; +import org.apache.qpid.transport.network.NetworkConnection; import org.apache.qpid.server.configuration.*; import org.apache.qpid.server.transport.ServerConnection; -import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.ConnectionMessages; import org.apache.qpid.server.registry.IApplicationRegistry; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.util.UUID; -public class ProtocolEngine_0_10 extends InputHandler implements ProtocolEngine, ConnectionConfig +public class ProtocolEngine_0_10 extends InputHandler implements ServerProtocolEngine, ConnectionConfig { public static final int MAX_FRAME_SIZE = 64 * 1024 - 1; - private NetworkDriver _networkDriver; + private NetworkConnection _network; private long _readBytes; private long _writtenBytes; private ServerConnection _connection; @@ -47,26 +48,22 @@ public class ProtocolEngine_0_10 extends InputHandler implements ProtocolEngine private long _createTime = System.currentTimeMillis(); public ProtocolEngine_0_10(ServerConnection conn, - NetworkDriver networkDriver, + NetworkConnection network, final IApplicationRegistry appRegistry) { super(new Assembler(conn)); _connection = conn; _connection.setConnectionConfig(this); - _networkDriver = networkDriver; + _id = appRegistry.getConfigStore().createId(); _appRegistry = appRegistry; - // FIXME Two log messages to maintain compatinbility with earlier protocol versions - _connection.getLogActor().message(ConnectionMessages.OPEN(null, null, false, false)); - _connection.getLogActor().message(ConnectionMessages.OPEN(null, "0-10", false, true)); - } + if(network != null) + { + setNetworkConnection(network); + } + - public void setNetworkDriver(NetworkDriver driver) - { - _networkDriver = driver; - Disassembler dis = new Disassembler(driver, MAX_FRAME_SIZE); - _connection.setSender(dis); _connection.onOpen(new Runnable() { public void run() @@ -77,14 +74,30 @@ public class ProtocolEngine_0_10 extends InputHandler implements ProtocolEngine } + public void setNetworkConnection(NetworkConnection network) + { + setNetworkConnection(network, network.getSender()); + } + + public void setNetworkConnection(NetworkConnection network, Sender<ByteBuffer> sender) + { + _network = network; + + _connection.setSender(new Disassembler(sender, MAX_FRAME_SIZE)); + + // FIXME Two log messages to maintain compatibility with earlier protocol versions + _connection.getLogActor().message(ConnectionMessages.OPEN(null, null, false, false)); + _connection.getLogActor().message(ConnectionMessages.OPEN(null, "0-10", false, true)); + } + public SocketAddress getRemoteAddress() { - return _networkDriver.getRemoteAddress(); + return _network.getRemoteAddress(); } public SocketAddress getLocalAddress() { - return _networkDriver.getLocalAddress(); + return _network.getLocalAddress(); } public long getReadBytes() @@ -134,7 +147,7 @@ public class ProtocolEngine_0_10 extends InputHandler implements ProtocolEngine public String getAuthId() { - return _connection.getAuthorizationID(); + return _connection.getAuthorizedPrincipal() == null ? null : _connection.getAuthorizedPrincipal().getName(); } public String getRemoteProcessName() @@ -193,9 +206,14 @@ public class ProtocolEngine_0_10 extends InputHandler implements ProtocolEngine { return false; } - + public void mgmtClose() { _connection.mgmtClose(); } + + public long getConnectionId() + { + return _connection.getConnectionId(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java index b6e97e08fb..371ae0de50 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java @@ -60,7 +60,7 @@ public class AMQPriorityQueue extends SimpleAMQQueue { // check that all subscriptions are not in advance of the entry SubscriptionList.SubscriptionNodeIterator subIter = _subscriptionList.iterator(); - while(subIter.advance() && !entry.isAcquired()) + while(subIter.advance() && entry.isAvailable()) { final Subscription subscription = subIter.getNode().getSubscription(); if(!subscription.isClosed()) @@ -70,7 +70,7 @@ public class AMQPriorityQueue extends SimpleAMQQueue { QueueEntry subnode = context._lastSeenEntry; QueueEntry released = context._releasedEntry; - while(subnode != null && entry.compareTo(subnode) < 0 && !entry.isAcquired() && (released == null || released.compareTo(entry) < 0)) + while(subnode != null && entry.compareTo(subnode) < 0 && entry.isAvailable() && (released == null || released.compareTo(entry) < 0)) { if(QueueContext._releasedUpdater.compareAndSet(context,released,entry)) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index de9dc42de8..9140a13625 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -21,21 +21,18 @@ package org.apache.qpid.server.queue; import org.apache.qpid.AMQException; -import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.configuration.QueueConfig; -import org.apache.qpid.server.configuration.QueueConfiguration; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeReferrer; import org.apache.qpid.server.management.Managable; import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.security.PrincipalHolder; +import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.store.TransactionLogResource; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.txn.ServerTransaction; @@ -72,8 +69,8 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer boolean isAutoDelete(); AMQShortString getOwner(); - PrincipalHolder getPrincipalHolder(); - void setPrincipalHolder(PrincipalHolder principalHolder); + AuthorizationHolder getAuthorizationHolder(); + void setAuthorizationHolder(AuthorizationHolder principalHolder); void setExclusiveOwningSession(AMQSessionModel owner); AMQSessionModel getExclusiveOwningSession(); @@ -108,23 +105,16 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>, ExchangeRefer boolean isDeleted(); - int delete() throws AMQException; - void requeue(QueueEntry entry); - void requeue(QueueEntryImpl storeContext, Subscription subscription); - void dequeue(QueueEntry entry, Subscription sub); void decrementUnackedMsgCount(); - boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException; - - void addQueueDeleteTask(final Task task); void removeQueueDeleteTask(final Task task); diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java index b5294b6d2f..c8eb118b11 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -43,6 +43,7 @@ import javax.management.JMException; import javax.management.MBeanException; import javax.management.MBeanNotificationInfo; import javax.management.Notification; +import javax.management.ObjectName; import javax.management.OperationsException; import javax.management.monitor.MonitorNotification; import javax.management.openmbean.ArrayType; @@ -97,7 +98,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que { super(ManagedQueue.class, ManagedQueue.TYPE); _queue = queue; - _queueName = jmxEncode(new StringBuffer(queue.getNameShortString()), 0).toString(); + _queueName = queue.getName(); } public ManagedObject getParentObject() @@ -147,7 +148,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que public String getObjectInstanceName() { - return _queueName; + return ObjectName.quote(_queueName); } public String getName() @@ -506,7 +507,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que private String[] getMessageHeaderProperties(ContentHeaderBody headerBody) { List<String> list = new ArrayList<String>(); - BasicContentHeaderProperties headerProperties = (BasicContentHeaderProperties) headerBody.properties; + BasicContentHeaderProperties headerProperties = (BasicContentHeaderProperties) headerBody.getProperties(); list.add("reply-to = " + headerProperties.getReplyToAsString()); list.add("propertyFlags = " + headerProperties.getPropertyFlags()); list.add("ApplicationID = " + headerProperties.getAppIdAsString()); diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java index 2d2fb3a214..a56f5685b8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java @@ -96,9 +96,9 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes public void setExpiration() { long expiration = - ((BasicContentHeaderProperties) _contentHeaderBody.properties).getExpiration(); + ((BasicContentHeaderProperties) _contentHeaderBody.getProperties()).getExpiration(); long timestamp = - ((BasicContentHeaderProperties) _contentHeaderBody.properties).getTimestamp(); + ((BasicContentHeaderProperties) _contentHeaderBody.getProperties()).getTimestamp(); if (SYNCHED_CLOCKS) { @@ -139,7 +139,7 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes public int addContentBodyFrame(final ContentChunk contentChunk) throws AMQException { - _storedMessageHandle.addContent((int)_bodyLengthReceived, contentChunk.getData().buf()); + _storedMessageHandle.addContent((int)_bodyLengthReceived, ByteBuffer.wrap(contentChunk.getData())); _bodyLengthReceived += contentChunk.getSize(); _contentChunks.add(contentChunk); @@ -193,8 +193,8 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes public boolean isPersistent() { - return getContentHeader().properties instanceof BasicContentHeaderProperties && - ((BasicContentHeaderProperties) getContentHeader().properties).getDeliveryMode() == + return getContentHeader().getProperties() instanceof BasicContentHeaderProperties && + ((BasicContentHeaderProperties) getContentHeader().getProperties()).getDeliveryMode() == BasicContentHeaderProperties.PERSISTENT; } @@ -263,7 +263,7 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes int written = 0; for(ContentChunk cb : _contentChunks) { - ByteBuffer data = cb.getData().buf(); + ByteBuffer data = ByteBuffer.wrap(cb.getData()); if(offset+written >= pos && offset < pos + data.limit()) { ByteBuffer src = data.duplicate(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java index edd1e0bdc3..be29245901 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java @@ -52,6 +52,17 @@ public interface QueueEntry extends Comparable<QueueEntry>, Filterable } public abstract State getState(); + + /** + * Returns true if state is either DEQUEUED or DELETED. + * + * @return true if state is either DEQUEUED or DELETED. + */ + public boolean isDispensed() + { + State currentState = getState(); + return currentState == State.DEQUEUED || currentState == State.DELETED; + } } @@ -191,11 +202,7 @@ public interface QueueEntry extends Comparable<QueueEntry>, Filterable void reject(); - void reject(Subscription subscription); - - boolean isRejectedBy(Subscription subscription); - - void requeue(Subscription subscription); + boolean isRejectedBy(long subscriptionId); void dequeue(); @@ -209,4 +216,18 @@ public interface QueueEntry extends Comparable<QueueEntry>, Filterable void addStateChangeListener(StateChangeListener listener); boolean removeStateChangeListener(StateChangeListener listener); + + /** + * Returns true if entry is in DEQUEUED state, otherwise returns false. + * + * @return true if entry is in DEQUEUED state, otherwise returns false + */ + boolean isDequeued(); + + /** + * Returns true if entry is either DEQUED or DELETED state. + * + * @return true if entry is either DEQUED or DELETED state + */ + boolean isDispensed(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index 1ba4f4d89b..5b57e40a82 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -51,7 +51,7 @@ public class QueueEntryImpl implements QueueEntry private MessageReference _message; - private Set<Subscription> _rejectedBy = null; + private Set<Long> _rejectedBy = null; private volatile EntryState _state = AVAILABLE_STATE; @@ -325,19 +325,16 @@ public class QueueEntryImpl implements QueueEntry public void reject() { - reject(getDeliveredSubscription()); - } + Subscription subscription = getDeliveredSubscription(); - public void reject(Subscription subscription) - { if (subscription != null) { if (_rejectedBy == null) { - _rejectedBy = new HashSet<Subscription>(); + _rejectedBy = new HashSet<Long>(); } - _rejectedBy.add(subscription); + _rejectedBy.add(subscription.getSubscriptionID()); } else { @@ -345,12 +342,12 @@ public class QueueEntryImpl implements QueueEntry } } - public boolean isRejectedBy(Subscription subscription) + public boolean isRejectedBy(long subscriptionId) { if (_rejectedBy != null) // We have subscriptions that rejected this message { - return _rejectedBy.contains(subscription); + return _rejectedBy.contains(subscriptionId); } else // This messasge hasn't been rejected yet. { @@ -358,15 +355,6 @@ public class QueueEntryImpl implements QueueEntry } } - public void requeue(Subscription subscription) - { - getQueue().requeue(this, subscription); - if(_stateChangeListeners != null) - { - notifyStateChange(QueueEntry.State.ACQUIRED, QueueEntry.State.AVAILABLE); - } - } - public void dequeue() { EntryState state = _state; @@ -508,7 +496,7 @@ public class QueueEntryImpl implements QueueEntry { QueueEntryImpl next = nextNode(); - while(next != null && next.isDeleted()) + while(next != null && next.isDispensed() ) { final QueueEntryImpl newNext = next.nextNode(); @@ -556,4 +544,14 @@ public class QueueEntryImpl implements QueueEntry return _queueEntryList; } + public boolean isDequeued() + { + return _state == DEQUEUED_STATE; + } + + public boolean isDispensed() + { + return _state.isDispensed(); + } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java new file mode 100644 index 0000000000..7e1d57e205 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRunner.java @@ -0,0 +1,84 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.pool.ReadWriteRunnable; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.queue.QueueRunner; +import org.apache.qpid.server.queue.SimpleAMQQueue; + +/** + * QueueRunners are Runnables used to process a queue when requiring + * asynchronous message delivery to subscriptions, which is necessary + * when straight-through delivery of a message to a subscription isn't + * possible during the enqueue operation. + */ +public class QueueRunner implements ReadWriteRunnable +{ + private static final Logger _logger = Logger.getLogger(QueueRunner.class); + + private final String _name; + private final SimpleAMQQueue _queue; + + public QueueRunner(SimpleAMQQueue queue, long count) + { + _queue = queue; + _name = "QueueRunner-" + count + "-" + queue.getLogActor(); + } + + public void run() + { + String originalName = Thread.currentThread().getName(); + try + { + Thread.currentThread().setName(_name); + CurrentActor.set(_queue.getLogActor()); + + _queue.processQueue(this); + } + catch (AMQException e) + { + _logger.error("Exception during asynchronous delivery by " + _name, e); + } + finally + { + CurrentActor.remove(); + Thread.currentThread().setName(originalName); + } + } + + public boolean isRead() + { + return false; + } + + public boolean isWrite() + { + return true; + } + + public String toString() + { + return _name; + } +}
\ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index b003152db6..a095ef47ea 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -44,7 +44,7 @@ import org.apache.qpid.server.logging.subjects.QueueLogSubject; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.PrincipalHolder; +import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionList; import org.apache.qpid.server.txn.AutoCommitTransaction; @@ -83,7 +83,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener /** null means shared */ private final AMQShortString _owner; - private PrincipalHolder _prinicpalHolder; + private AuthorizationHolder _authorizationHolder; private boolean _exclusive = false; private AMQSessionModel _exclusiveOwner; @@ -102,9 +102,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener protected final QueueEntryList _entries; - protected final SubscriptionList _subscriptionList = new SubscriptionList(this); - - private final AtomicReference<SubscriptionList.SubscriptionNode> _lastSubscriptionNode = new AtomicReference<SubscriptionList.SubscriptionNode>(_subscriptionList.getHead()); + protected final SubscriptionList _subscriptionList = new SubscriptionList(); private volatile Subscription _exclusiveSubscriber; @@ -373,14 +371,14 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener return _owner; } - public PrincipalHolder getPrincipalHolder() + public AuthorizationHolder getAuthorizationHolder() { - return _prinicpalHolder; + return _authorizationHolder; } - public void setPrincipalHolder(PrincipalHolder prinicpalHolder) + public void setAuthorizationHolder(final AuthorizationHolder authorizationHolder) { - _prinicpalHolder = prinicpalHolder; + _authorizationHolder = authorizationHolder; } @@ -602,25 +600,25 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener iterate over subscriptions and if any is at the end of the queue and can deliver this message, then deliver the message */ - SubscriptionList.SubscriptionNode node = _lastSubscriptionNode.get(); - SubscriptionList.SubscriptionNode nextNode = node.getNext(); + SubscriptionList.SubscriptionNode node = _subscriptionList.getMarkedNode(); + SubscriptionList.SubscriptionNode nextNode = node.findNext(); if (nextNode == null) { - nextNode = _subscriptionList.getHead().getNext(); + nextNode = _subscriptionList.getHead().findNext(); } while (nextNode != null) { - if (_lastSubscriptionNode.compareAndSet(node, nextNode)) + if (_subscriptionList.updateMarkedNode(node, nextNode)) { break; } else { - node = _lastSubscriptionNode.get(); - nextNode = node.getNext(); + node = _subscriptionList.getMarkedNode(); + nextNode = node.findNext(); if (nextNode == null) { - nextNode = _subscriptionList.getHead().getNext(); + nextNode = _subscriptionList.getHead().findNext(); } } } @@ -629,7 +627,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener // this catches the case where we *just* miss an update int loops = 2; - while (!(entry.isAcquired() || entry.isDeleted()) && loops != 0) + while (entry.isAvailable() && loops != 0) { if (nextNode == null) { @@ -642,13 +640,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener Subscription sub = nextNode.getSubscription(); deliverToSubscription(sub, entry); } - nextNode = nextNode.getNext(); + nextNode = nextNode.findNext(); } } - if (!(entry.isAcquired() || entry.isDeleted())) + if (entry.isAvailable()) { checkSubscriptionsNotAheadOfDelivery(entry); @@ -805,24 +803,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } - public void requeue(QueueEntryImpl entry, Subscription subscription) - { - SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); - // iterate over all the subscribers, and if they are in advance of this queue entry then move them backwards - while (subscriberIter.advance()) - { - Subscription sub = subscriberIter.getNode().getSubscription(); - - // we don't make browsers send the same stuff twice - if (sub.seesRequeues() && (!sub.acquires() && sub == subscription)) - { - updateSubRequeueEntry(sub, entry); - } - } - - deliverAsync(); - } - public void dequeue(QueueEntry entry, Subscription sub) { decrementQueueCount(); @@ -960,7 +940,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener while (queueListIterator.advance()) { QueueEntry node = queueListIterator.getNode(); - if (node != null && !node.isDeleted()) + if (node != null && !node.isDispensed()) { entryList.add(node); } @@ -1064,7 +1044,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener while (queueListIterator.advance() && !filter.filterComplete()) { QueueEntry node = queueListIterator.getNode(); - if (!node.isDeleted() && filter.accept(node)) + if (!node.isDispensed() && filter.accept(node)) { entryList.add(node); } @@ -1258,7 +1238,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener if ((messageId >= fromMessageId) && (messageId <= toMessageId) - && !node.isDeleted() && node.acquire()) { dequeueEntry(node); @@ -1288,7 +1267,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener while (noDeletes && queueListIterator.advance()) { QueueEntry node = queueListIterator.getNode(); - if (!node.isDeleted() && node.acquire()) + if (node.acquire()) { dequeueEntry(node); noDeletes = false; @@ -1318,7 +1297,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener while (queueListIterator.advance()) { QueueEntry node = queueListIterator.getNode(); - if (!node.isDeleted() && node.acquire()) + if (node.acquire()) { dequeueEntry(node, txn); if(++count == request) @@ -1585,7 +1564,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener public void deliverAsync() { - Runner runner = new Runner(_stateChangeCount.incrementAndGet()); + QueueRunner runner = new QueueRunner(this, _stateChangeCount.incrementAndGet()); if (_asynchronousRunner.compareAndSet(null, runner)) { @@ -1604,52 +1583,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener _asyncDelivery.execute(flusher); } - - 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) - { - _logger.error(e); - } - finally - { - CurrentActor.remove(); - Thread.currentThread().setName(originalName); - } - } - - public boolean isRead() - { - return false; - } - - public boolean isWrite() - { - return true; - } - - public String toString() - { - return _name; - } - } - public void flushSubscription(Subscription sub) throws AMQException { // Access control @@ -1718,7 +1651,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener QueueEntry node = getNextAvailableEntry(sub); - if (node != null && !(node.isAcquired() || node.isDeleted())) + if (node != null && node.isAvailable()) { if (sub.hasInterest(node)) { @@ -1779,7 +1712,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener QueueEntry node = (releasedNode != null && lastSeen.compareTo(releasedNode)>=0) ? releasedNode : _entries.next(lastSeen); boolean expired = false; - while (node != null && (node.isAcquired() || node.isDeleted() || (expired = node.expired()) || !sub.hasInterest(node))) + while (node != null && (!node.isAvailable() || (expired = node.expired()) || !sub.hasInterest(node))) { if (expired) { @@ -1808,14 +1741,40 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } - private void processQueue(Runnable runner) throws AMQException + /** + * Used by queue Runners to asynchronously deliver messages to consumers. + * + * A queue Runner is started whenever a state change occurs, e.g when a new + * message arrives on the queue and cannot be immediately delivered to a + * subscription (i.e. asynchronous delivery is required). Unless there are + * SubFlushRunners operating (due to subscriptions unsuspending) which are + * capable of accepting/delivering all messages then these messages would + * otherwise remain on the queue. + * + * processQueue should be running while there are messages on the queue AND + * there are subscriptions that can deliver them. If there are no + * subscriptions capable of delivering the remaining messages on the queue + * then processQueue should stop to prevent spinning. + * + * Since processQueue is runs in a fixed size Executor, it should not run + * indefinitely to prevent starving other tasks of CPU (e.g jobs to process + * incoming messages may not be able to be scheduled in the thread pool + * because all threads are working on clearing down large queues). To solve + * this problem, after an arbitrary number of message deliveries the + * processQueue job stops iterating, resubmits itself to the executor, and + * ends the current instance + * + * @param runner the Runner to schedule + * @throws AMQException + */ + public void processQueue(QueueRunner runner) throws AMQException { long stateChangeCount; long previousStateChangeCount = Long.MIN_VALUE; boolean deliveryIncomplete = true; - int extraLoops = 1; - long iterations = MAX_ASYNC_DELIVERIES; + boolean lastLoop = false; + int iterations = MAX_ASYNC_DELIVERIES; _asynchronousRunner.compareAndSet(runner, null); @@ -1832,12 +1791,14 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener if (previousStateChangeCount != stateChangeCount) { - extraLoops = 1; + //further asynchronous delivery is required since the + //previous loop. keep going if iteration slicing allows. + lastLoop = false; } previousStateChangeCount = stateChangeCount; - deliveryIncomplete = _subscriptionList.size() != 0; - boolean done; + boolean allSubscriptionsDone = true; + boolean subscriptionDone; SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator(); //iterate over the subscribers and try to advance their pointer @@ -1847,30 +1808,25 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener sub.getSendLock(); try { - - done = attemptDelivery(sub); - - if (done) + //attempt delivery. returns true if no further delivery currently possible to this sub + subscriptionDone = attemptDelivery(sub); + if (subscriptionDone) { - if (extraLoops == 0) + //close autoClose subscriptions if we are not currently intent on continuing + if (lastLoop && sub.isAutoClose()) { - deliveryIncomplete = false; - if (sub.isAutoClose()) - { - unregisterSubscription(sub); + unregisterSubscription(sub); - sub.confirmAutoClose(); - } - } - else - { - extraLoops--; + sub.confirmAutoClose(); } } else { + //this subscription can accept additional deliveries, so we must + //keep going after this (if iteration slicing allows it) + allSubscriptionsDone = false; + lastLoop = false; iterations--; - extraLoops = 1; } } finally @@ -1878,10 +1834,34 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener sub.releaseSendLock(); } } + + if(allSubscriptionsDone && lastLoop) + { + //We have done an extra loop already and there are again + //again no further delivery attempts possible, only + //keep going if state change demands it. + deliveryIncomplete = false; + } + else if(allSubscriptionsDone) + { + //All subscriptions reported being done, but we have to do + //an extra loop if the iterations are not exhausted and + //there is still any work to be done + deliveryIncomplete = _subscriptionList.size() != 0; + lastLoop = true; + } + else + { + //some subscriptions can still accept more messages, + //keep going if iteration count allows. + lastLoop = false; + deliveryIncomplete = true; + } + _asynchronousRunner.set(null); } - // If deliveries == 0 then the limitting factor was the time-slicing rather than available messages or credit + // If iterations == 0 then the limiting factor was the time-slicing rather than available messages or credit // therefore we should schedule this runner again (unless someone beats us to it :-) ). if (iterations == 0 && _asynchronousRunner.compareAndSet(null, runner)) { @@ -1901,8 +1881,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener while (queueListIterator.advance()) { QueueEntry node = queueListIterator.getNode(); - // Only process nodes that are not currently deleted - if (!node.isDeleted()) + // Only process nodes that are not currently deleted and not dequeued + if (!node.isDispensed()) { // If the node has exired then aquire it if (node.expired() && node.acquire()) @@ -2242,4 +2222,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener } } } + + public LogActor getLogActor() + { + return _logActor; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java index b97c2c55c5..46baab8c85 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java @@ -1,6 +1,5 @@ package org.apache.qpid.server.queue; -import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.ServerMessage; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; @@ -156,7 +155,7 @@ public class SimpleQueueEntryList implements QueueEntryList if(!atTail()) { QueueEntryImpl nextNode = _lastNode.nextNode(); - while(nextNode.isDeleted() && nextNode.nextNode() != null) + while(nextNode.isDispensed() && nextNode.nextNode() != null) { nextNode = nextNode.nextNode(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 78a642f22f..c07074f69c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -21,9 +21,14 @@ package org.apache.qpid.server.registry; import java.net.InetSocketAddress; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; @@ -41,23 +46,27 @@ import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.logging.CompositeStartupMessageLogger; import org.apache.qpid.server.logging.Log4jMessageLogger; import org.apache.qpid.server.logging.RootMessageLogger; -import org.apache.qpid.server.logging.AbstractRootMessageLogger; import org.apache.qpid.server.logging.SystemOutMessageLogger; +import org.apache.qpid.server.logging.actors.AbstractActor; import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.BrokerMessages; +import org.apache.qpid.server.logging.messages.VirtualHostMessages; import org.apache.qpid.server.management.ManagedObjectRegistry; import org.apache.qpid.server.management.NoopManagedObjectRegistry; +import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.auth.database.ConfigurationFilePrincipalDatabaseManager; -import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; +import org.apache.qpid.server.security.SecurityManager.SecurityConfiguration; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.security.auth.manager.AuthenticationManagerPluginFactory; +import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.transport.QpidAcceptor; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostImpl; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; +import org.osgi.framework.BundleContext; + /** * An abstract application registry that provides access to configuration information and handles the @@ -69,12 +78,10 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { protected static final Logger _logger = Logger.getLogger(ApplicationRegistry.class); - private static Map<Integer, IApplicationRegistry> _instanceMap = new HashMap<Integer, IApplicationRegistry>(); + private static AtomicReference<IApplicationRegistry> _instance = new AtomicReference<IApplicationRegistry>(null); protected final ServerConfiguration _configuration; - public static final int DEFAULT_INSTANCE = 1; - protected final Map<InetSocketAddress, QpidAcceptor> _acceptors = new HashMap<InetSocketAddress, QpidAcceptor>(); protected ManagedObjectRegistry _managedObjectRegistry; @@ -85,8 +92,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry protected SecurityManager _securityManager; - protected PrincipalDatabaseManager _databaseManager; - protected PluginManager _pluginManager; protected ConfigurationManager _configurationManager; @@ -102,8 +107,12 @@ public abstract class ApplicationRegistry implements IApplicationRegistry private BrokerConfig _broker; private ConfigStore _configStore; + + private Timer _reportingTimer; + private boolean _statisticsEnabled = false; + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; - protected String _registryName; + private BundleContext _bundleContext; static { @@ -114,53 +123,54 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { public void run() { - removeAll(); + remove(); } } public static void initialise(IApplicationRegistry instance) throws Exception { - initialise(instance, DEFAULT_INSTANCE); - } + if(instance == null) + { + throw new IllegalArgumentException("ApplicationRegistry instance must not be null"); + } - @SuppressWarnings("finally") - public static void initialise(IApplicationRegistry instance, int instanceID) throws Exception - { - if (instance != null) + if(!_instance.compareAndSet(null, instance)) { - _logger.info("Initialising Application Registry(" + instance + "):" + instanceID); - _instanceMap.put(instanceID, instance); + throw new IllegalStateException("An ApplicationRegistry is already initialised"); + } + + _logger.info("Initialising Application Registry(" + instance + ")"); + + + final ConfigStore store = ConfigStore.newInstance(); + store.setRoot(new SystemConfigImpl(store)); + instance.setConfigStore(store); - final ConfigStore store = ConfigStore.newInstance(); - store.setRoot(new SystemConfigImpl(store)); - instance.setConfigStore(store); + BrokerConfig broker = new BrokerConfigAdapter(instance); - BrokerConfig broker = new BrokerConfigAdapter(instance); + SystemConfig system = (SystemConfig) store.getRoot(); + system.addBroker(broker); + instance.setBroker(broker); - SystemConfig system = (SystemConfig) store.getRoot(); - system.addBroker(broker); - instance.setBroker(broker); + try + { + instance.initialise(); + } + catch (Exception e) + { + _instance.set(null); + //remove the Broker instance, then re-throw try { - instance.initialise(instanceID); + system.removeBroker(broker); } - catch (Exception e) + catch(Throwable t) { - _instanceMap.remove(instanceID); - try - { - system.removeBroker(broker); - } - finally - { - throw e; - } + //ignore } - } - else - { - remove(instanceID); + + throw e; } } @@ -176,35 +186,19 @@ public abstract class ApplicationRegistry implements IApplicationRegistry public static boolean isConfigured() { - return isConfigured(DEFAULT_INSTANCE); - } - - public static boolean isConfigured(int instanceID) - { - return _instanceMap.containsKey(instanceID); + return _instance.get() != null; } - /** Method to cleanly shutdown the default registry running in this JVM */ public static void remove() { - remove(DEFAULT_INSTANCE); - } - - /** - * Method to cleanly shutdown specified registry running in this JVM - * - * @param instanceID the instance to shutdown - */ - public static void remove(int instanceID) - { + IApplicationRegistry instance = _instance.getAndSet(null); try { - IApplicationRegistry instance = _instanceMap.get(instanceID); if (instance != null) { if (_logger.isInfoEnabled()) { - _logger.info("Shutting down ApplicationRegistry(" + instanceID + "):" + instance); + _logger.info("Shutting down ApplicationRegistry(" + instance + ")"); } instance.close(); instance.getBroker().getSystem().removeBroker(instance.getBroker()); @@ -212,27 +206,19 @@ public abstract class ApplicationRegistry implements IApplicationRegistry } catch (Exception e) { - _logger.error("Error shutting down Application Registry(" + instanceID + "): " + e, e); - } - finally - { - _instanceMap.remove(instanceID); + _logger.error("Error shutting down Application Registry(" + instance + "): " + e, e); } } - /** Method to cleanly shutdown all registries currently running in this JVM */ - public static void removeAll() + protected ApplicationRegistry(ServerConfiguration configuration) { - Object[] keys = _instanceMap.keySet().toArray(); - for (Object k : keys) - { - remove((Integer) k); - } + this(configuration, null); } - protected ApplicationRegistry(ServerConfiguration configuration) + protected ApplicationRegistry(ServerConfiguration configuration, BundleContext bundleContext) { _configuration = configuration; + _bundleContext = bundleContext; } public void configure() throws ConfigurationException @@ -241,7 +227,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { - _pluginManager = new PluginManager(_configuration.getPluginDirectory(), _configuration.getCacheDirectory()); + _pluginManager = new PluginManager(_configuration.getPluginDirectory(), _configuration.getCacheDirectory(), _bundleContext); } catch (Exception e) { @@ -251,11 +237,10 @@ public abstract class ApplicationRegistry implements IApplicationRegistry _configuration.initialise(); } - public void initialise(int instanceID) throws Exception + public void initialise() throws Exception { //Create the RootLogger to be used during broker operation _rootMessageLogger = new Log4jMessageLogger(_configuration); - _registryName = String.valueOf(instanceID); //Create the composite (log4j+SystemOut MessageLogger to be used during startup RootMessageLogger[] messageLoggers = {new SystemOutMessageLogger(), _rootMessageLogger}; @@ -277,11 +262,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry _securityManager = new SecurityManager(_configuration, _pluginManager); - createDatabaseManager(_configuration); - - _authenticationManager = new PrincipalDatabaseAuthenticationManager(null, null); - - _databaseManager.initialiseManagement(_configuration); + _authenticationManager = createAuthenticationManager(); _managedObjectRegistry.start(); } @@ -294,6 +275,8 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { initialiseVirtualHosts(); + initialiseStatistics(); + initialiseStatisticsReporting(); } finally { @@ -302,9 +285,51 @@ public abstract class ApplicationRegistry implements IApplicationRegistry } } - protected void createDatabaseManager(ServerConfiguration configuration) throws Exception + /** + * Iterates across all discovered authentication manager factories, offering the security configuration to each. + * Expects <b>exactly</b> one authentication manager to configure and initialise itself. + * + * It is an error to configure more than one authentication manager, or to configure none. + * + * @return authentication manager + * @throws ConfigurationException + */ + protected AuthenticationManager createAuthenticationManager() throws ConfigurationException { - _databaseManager = new ConfigurationFilePrincipalDatabaseManager(_configuration); + final SecurityConfiguration securityConfiguration = _configuration.getConfiguration(SecurityConfiguration.class.getName()); + final Collection<AuthenticationManagerPluginFactory<? extends Plugin>> factories = _pluginManager.getAuthenticationManagerPlugins().values(); + + if (factories.size() == 0) + { + throw new ConfigurationException("No authentication manager factory plugins found. Check the desired authentication" + + "manager plugin has been placed in the plugins directory."); + } + + AuthenticationManager authMgr = null; + + for (final Iterator<AuthenticationManagerPluginFactory<? extends Plugin>> iterator = factories.iterator(); iterator.hasNext();) + { + final AuthenticationManagerPluginFactory<? extends Plugin> factory = (AuthenticationManagerPluginFactory<? extends Plugin>) iterator.next(); + final AuthenticationManager tmp = factory.newInstance(securityConfiguration); + if (tmp != null) + { + if (authMgr != null) + { + throw new ConfigurationException("Cannot configure more than one authentication manager." + + " Both " + tmp.getClass() + " and " + authMgr.getClass() + " are configured." + + " Remove configuration for one of the authentication manager, or remove the plugin JAR" + + " from the classpath."); + } + authMgr = tmp; + } + } + + if (authMgr == null) + { + throw new ConfigurationException("No authentication managers configured within the configure file."); + } + + return authMgr; } protected void initialiseVirtualHosts() throws Exception @@ -320,26 +345,88 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { _managedObjectRegistry = new NoopManagedObjectRegistry(); } - - public static IApplicationRegistry getInstance() + + public void initialiseStatisticsReporting() { - return getInstance(DEFAULT_INSTANCE); + long report = _configuration.getStatisticsReportingPeriod() * 1000; // convert to ms + final boolean broker = _configuration.isStatisticsGenerationBrokerEnabled(); + final boolean virtualhost = _configuration.isStatisticsGenerationVirtualhostsEnabled(); + final boolean reset = _configuration.isStatisticsReportResetEnabled(); + + /* add a timer task to report statistics if generation is enabled for broker or virtualhosts */ + if (report > 0L && (broker || virtualhost)) + { + _reportingTimer = new Timer("Statistics-Reporting", true); + + class StatisticsReportingTask extends TimerTask + { + private final int DELIVERED = 0; + private final int RECEIVED = 1; + + public void run() + { + CurrentActor.set(new AbstractActor(ApplicationRegistry.getInstance().getRootMessageLogger()) { + public String getLogMessage() + { + return "[" + Thread.currentThread().getName() + "] "; + } + }); + + if (broker) + { + CurrentActor.get().message(BrokerMessages.STATS_DATA(DELIVERED, _dataDelivered.getPeak() / 1024.0, _dataDelivered.getTotal())); + CurrentActor.get().message(BrokerMessages.STATS_MSGS(DELIVERED, _messagesDelivered.getPeak(), _messagesDelivered.getTotal())); + CurrentActor.get().message(BrokerMessages.STATS_DATA(RECEIVED, _dataReceived.getPeak() / 1024.0, _dataReceived.getTotal())); + CurrentActor.get().message(BrokerMessages.STATS_MSGS(RECEIVED, _messagesReceived.getPeak(), _messagesReceived.getTotal())); + } + + if (virtualhost) + { + for (VirtualHost vhost : getVirtualHostRegistry().getVirtualHosts()) + { + String name = vhost.getName(); + StatisticsCounter dataDelivered = vhost.getDataDeliveryStatistics(); + StatisticsCounter messagesDelivered = vhost.getMessageDeliveryStatistics(); + StatisticsCounter dataReceived = vhost.getDataReceiptStatistics(); + StatisticsCounter messagesReceived = vhost.getMessageReceiptStatistics(); + + CurrentActor.get().message(VirtualHostMessages.STATS_DATA(name, DELIVERED, dataDelivered.getPeak() / 1024.0, dataDelivered.getTotal())); + CurrentActor.get().message(VirtualHostMessages.STATS_MSGS(name, DELIVERED, messagesDelivered.getPeak(), messagesDelivered.getTotal())); + CurrentActor.get().message(VirtualHostMessages.STATS_DATA(name, RECEIVED, dataReceived.getPeak() / 1024.0, dataReceived.getTotal())); + CurrentActor.get().message(VirtualHostMessages.STATS_MSGS(name, RECEIVED, messagesReceived.getPeak(), messagesReceived.getTotal())); + } + } + + if (reset) + { + resetStatistics(); + } + + CurrentActor.remove(); + } + } + + _reportingTimer.scheduleAtFixedRate(new StatisticsReportingTask(), + report / 2, + report); + } } - public static IApplicationRegistry getInstance(int instanceID) + /** + * Get the ApplicationRegistry + * @return the IApplicationRegistry instance + * @throws IllegalStateException if no registry instance has been initialised. + */ + public static IApplicationRegistry getInstance() throws IllegalStateException { - synchronized (IApplicationRegistry.class) + IApplicationRegistry iApplicationRegistry = _instance.get(); + if (iApplicationRegistry == null) { - IApplicationRegistry instance = _instanceMap.get(instanceID); - - if (instance == null) - { - throw new IllegalStateException("Application Registry (" + instanceID + ") not created"); - } - else - { - return instance; - } + throw new IllegalStateException("No ApplicationRegistry has been initialised"); + } + else + { + return iApplicationRegistry; } } @@ -369,6 +456,12 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { _logger.info("Shutting down ApplicationRegistry:" + this); } + + //Stop Statistics Reporting + if (_reportingTimer != null) + { + _reportingTimer.cancel(); + } //Stop incoming connections unbind(); @@ -376,10 +469,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry //Shutdown virtualhosts close(_virtualHostRegistry); -// close(_accessManager); -// -// close(_databaseManager); - close(_authenticationManager); close(_managedObjectRegistry); @@ -401,7 +490,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { - acceptor.getNetworkDriver().close(); + acceptor.getNetworkTransport().close(); } catch (Throwable e) { @@ -441,11 +530,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry return _managedObjectRegistry; } - public PrincipalDatabaseManager getDatabaseManager() - { - return _databaseManager; - } - public AuthenticationManager getAuthenticationManager() { return _authenticationManager; @@ -493,9 +577,81 @@ public abstract class ApplicationRegistry implements IApplicationRegistry public VirtualHost createVirtualHost(final VirtualHostConfiguration vhostConfig) throws Exception { - VirtualHostImpl virtualHost = new VirtualHostImpl(this, vhostConfig); + VirtualHostImpl virtualHost = new VirtualHostImpl(this, vhostConfig, null); _virtualHostRegistry.registerVirtualHost(virtualHost); getBroker().addVirtualHost(virtualHost); return virtualHost; } + + public void registerMessageDelivered(long messageSize) + { + if (isStatisticsEnabled()) + { + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); + } + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + if (isStatisticsEnabled()) + { + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); + } + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return _messagesReceived; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return _dataReceived; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return _messagesDelivered; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return _dataDelivered; + } + + public void resetStatistics() + { + _messagesDelivered.reset(); + _dataDelivered.reset(); + _messagesReceived.reset(); + _dataReceived.reset(); + + for (VirtualHost vhost : _virtualHostRegistry.getVirtualHosts()) + { + vhost.resetStatistics(); + } + } + + public void initialiseStatistics() + { + setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && + getConfiguration().isStatisticsGenerationBrokerEnabled()); + + _messagesDelivered = new StatisticsCounter("messages-delivered"); + _dataDelivered = new StatisticsCounter("bytes-delivered"); + _messagesReceived = new StatisticsCounter("messages-received"); + _dataReceived = new StatisticsCounter("bytes-received"); + } + + public boolean isStatisticsEnabled() + { + return _statisticsEnabled; + } + + public void setStatisticsEnabled(boolean enabled) + { + _statisticsEnabled = enabled; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java b/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java index 4a4253153c..108533ef96 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java @@ -71,7 +71,7 @@ public class BrokerConfigAdapter implements BrokerConfig public Integer getWorkerThreads() { - return _instance.getConfiguration().getProcessors(); + return _instance.getConfiguration().getConnectorProcessors(); } public Integer getMaxConnections() diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java index ff2a8c959b..9121f8f927 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -29,12 +29,18 @@ import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.management.JMXManagedObjectRegistry; import org.apache.qpid.server.management.NoopManagedObjectRegistry; +import org.osgi.framework.BundleContext; public class ConfigurationFileApplicationRegistry extends ApplicationRegistry { public ConfigurationFileApplicationRegistry(File configurationURL) throws ConfigurationException { - super(new ServerConfiguration(configurationURL)); + this(configurationURL, null); + } + + public ConfigurationFileApplicationRegistry(File configurationURL, BundleContext bundleContext) throws ConfigurationException + { + super(new ServerConfiguration(configurationURL), bundleContext); } @Override diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java index 228c3b9112..c27e0d19ec 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java @@ -33,21 +33,20 @@ import org.apache.qpid.server.logging.RootMessageLogger; import org.apache.qpid.server.management.ManagedObjectRegistry; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.transport.QpidAcceptor; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -public interface IApplicationRegistry +public interface IApplicationRegistry extends StatisticsGatherer { /** * 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(int instanceID) throws Exception; + void initialise() throws Exception; /** * Shutdown this Registry @@ -63,8 +62,6 @@ public interface IApplicationRegistry ManagedObjectRegistry getManagedObjectRegistry(); - PrincipalDatabaseManager getDatabaseManager(); - AuthenticationManager getAuthenticationManager(); VirtualHostRegistry getVirtualHostRegistry(); @@ -97,4 +94,6 @@ public interface IApplicationRegistry ConfigStore getConfigStore(); void setConfigStore(ConfigStore store); + + void initialiseStatisticsReporting(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/AuthorizationHolder.java b/java/broker/src/main/java/org/apache/qpid/server/security/AuthorizationHolder.java new file mode 100755 index 0000000000..3d8c77a86f --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/AuthorizationHolder.java @@ -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. + * + */ +package org.apache.qpid.server.security; + +import java.security.Principal; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.security.auth.sasl.GroupPrincipal; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; + +/** + * Represents the authorization of the logged on user. + * + */ +public interface AuthorizationHolder +{ + /** + * Returns the {@link Subject} of the authorized user. This is guaranteed to + * contain at least one {@link UsernamePrincipal}, representing the the identity + * used when the user logged on to the application, and zero or more {@link GroupPrincipal} + * representing the group(s) to which the user belongs. + * + * @return the Subject + */ + Subject getAuthorizedSubject(); + + /** + * Returns the {@link Principal} representing the the identity + * used when the user logged on to the application. + * + * @return a Principal + */ + Principal getAuthorizedPrincipal(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java index f18c327692..f582fed6a0 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java @@ -18,8 +18,19 @@ */ package org.apache.qpid.server.security; -import static org.apache.qpid.server.security.access.ObjectType.*; -import static org.apache.qpid.server.security.access.Operation.*; +import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE; +import static org.apache.qpid.server.security.access.ObjectType.METHOD; +import static org.apache.qpid.server.security.access.ObjectType.OBJECT; +import static org.apache.qpid.server.security.access.ObjectType.QUEUE; +import static org.apache.qpid.server.security.access.ObjectType.VIRTUALHOST; +import static org.apache.qpid.server.security.access.Operation.ACCESS; +import static org.apache.qpid.server.security.access.Operation.BIND; +import static org.apache.qpid.server.security.access.Operation.CONSUME; +import static org.apache.qpid.server.security.access.Operation.CREATE; +import static org.apache.qpid.server.security.access.Operation.DELETE; +import static org.apache.qpid.server.security.access.Operation.PUBLISH; +import static org.apache.qpid.server.security.access.Operation.PURGE; +import static org.apache.qpid.server.security.access.Operation.UNBIND; import java.net.SocketAddress; import java.security.Principal; @@ -29,6 +40,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import javax.security.auth.Subject; + import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; @@ -37,11 +50,9 @@ import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.plugins.PluginManager; -import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.security.access.ObjectProperties; import org.apache.qpid.server.security.access.Operation; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; /** * The security manager contains references to all loaded {@link SecurityPlugin}s and delegates security decisions to them based @@ -55,7 +66,7 @@ public class SecurityManager private static final Logger _logger = Logger.getLogger(SecurityManager.class); /** Container for the {@link Principal} that is using to this thread. */ - private static final ThreadLocal<Principal> _principal = new ThreadLocal<Principal>(); + private static final ThreadLocal<Subject> _subject = new ThreadLocal<Subject>(); private PluginManager _pluginManager; private Map<String, SecurityPluginFactory> _pluginFactories = new HashMap<String, SecurityPluginFactory>(); @@ -126,19 +137,14 @@ public class SecurityManager configureHostPlugins(configuration); } - public static Principal getThreadPrincipal() - { - return _principal.get(); - } - - public static void setThreadPrincipal(Principal principal) + public static Subject getThreadSubject() { - _principal.set(principal); + return _subject.get(); } - public static void setThreadPrincipal(String authId) + public static void setThreadSubject(final Subject subject) { - setThreadPrincipal(new UsernamePrincipal(authId)); + _subject.set(subject); } public void configureHostPlugins(ConfigurationPlugin hostConfig) throws ConfigurationException diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java b/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java index 70a9ea5356..e4bf8df340 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java @@ -149,9 +149,9 @@ public class ObjectProperties extends HashMap<ObjectProperties.Property, String> { put(Property.OWNER, queue.getOwner()); } - else if (queue.getPrincipalHolder() != null) + else if (queue.getAuthorizationHolder() != null) { - put(Property.OWNER, queue.getPrincipalHolder().getPrincipal().getName()); + put(Property.OWNER, queue.getAuthorizationHolder().getAuthorizedPrincipal().getName()); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java index 62967ef7eb..8c2d60a660 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java @@ -20,42 +20,93 @@ */ package org.apache.qpid.server.security.auth; +import javax.security.auth.Subject; + +/** + * Encapsulates the result of an attempt to authenticate. + * <p> + * The authentication status describes the overall outcome. + * <p> + * <ol> + * <li>If authentication status is SUCCESS, the subject will be populated. + * </li> + * <li>If authentication status is CONTINUE, the authentication has failed because the user + * supplied incorrect credentials (etc). If the authentication requires it, the next challenge + * is made available. + * </li> + * <li>If authentication status is ERROR , the authentication decision could not be made due + * to a failure (such as an external system), the {@link AuthenticationResult#getCause()} + * will provide the underlying exception. + * </li> + * </ol> + * + */ public class AuthenticationResult { public enum AuthenticationStatus { - SUCCESS, CONTINUE, ERROR + /** Authentication successful */ + SUCCESS, + /** Authentication not successful due to credentials problem etc */ + CONTINUE, + /** Problem prevented the authentication from being made e.g. failure of an external system */ + ERROR } - public AuthenticationStatus status; - public byte[] challenge; - - private Exception cause; + public final AuthenticationStatus _status; + public final byte[] _challenge; + private final Exception _cause; + private final Subject _subject; - public AuthenticationResult(AuthenticationStatus status) + public AuthenticationResult(final AuthenticationStatus status) { this(null, status, null); } - public AuthenticationResult(byte[] challenge, AuthenticationStatus status) + public AuthenticationResult(final byte[] challenge, final AuthenticationStatus status) { this(challenge, status, null); } - public AuthenticationResult(AuthenticationStatus error, Exception cause) + public AuthenticationResult(final AuthenticationStatus error, final Exception cause) { this(null, error, cause); } - public AuthenticationResult(byte[] challenge, AuthenticationStatus status, Exception cause) + public AuthenticationResult(final byte[] challenge, final AuthenticationStatus status, final Exception cause) + { + this._status = status; + this._challenge = challenge; + this._cause = cause; + this._subject = null; + } + + public AuthenticationResult(final Subject subject) { - this.status = status; - this.challenge = challenge; - this.cause = cause; + this._status = AuthenticationStatus.SUCCESS; + this._challenge = null; + this._cause = null; + this._subject = subject; } public Exception getCause() { - return cause; + return _cause; + } + + public AuthenticationStatus getStatus() + { + return _status; + } + + public byte[] getChallenge() + { + return _challenge; } + + public Subject getSubject() + { + return _subject; + } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java deleted file mode 100644 index 5cebb7d2d8..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/ConfigurationFilePrincipalDatabaseManager.java +++ /dev/null @@ -1,221 +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.auth.database; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; - -import org.apache.log4j.Logger; - -import org.apache.qpid.configuration.PropertyUtils; -import org.apache.qpid.configuration.PropertyException; -import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.database.PrincipalDatabase; -import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; -import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean; -import org.apache.qpid.AMQException; - -import javax.management.JMException; - -public class ConfigurationFilePrincipalDatabaseManager implements PrincipalDatabaseManager -{ - private static final Logger _logger = Logger.getLogger(ConfigurationFilePrincipalDatabaseManager.class); - - Map<String, PrincipalDatabase> _databases; - - public ConfigurationFilePrincipalDatabaseManager(ServerConfiguration _configuration) throws Exception - { - _logger.info("Initialising PrincipalDatabase authentication manager"); - _databases = initialisePrincipalDatabases(_configuration); - } - - private Map<String, PrincipalDatabase> initialisePrincipalDatabases(ServerConfiguration _configuration) throws Exception - { - List<String> databaseNames = _configuration.getPrincipalDatabaseNames(); - List<String> databaseClasses = _configuration.getPrincipalDatabaseClass(); - Map<String, PrincipalDatabase> databases = new HashMap<String, PrincipalDatabase>(); - - if (databaseNames.size() == 0) - { - _logger.warn("No Principal databases specified. Broker running with NO AUTHENTICATION"); - } - - for (int i = 0; i < databaseNames.size(); i++) - { - Object o; - try - { - o = Class.forName(databaseClasses.get(i)).newInstance(); - } - catch (Exception e) - { - throw new Exception("Error initialising principal database: " + e, e); - } - - if (!(o instanceof PrincipalDatabase)) - { - throw new Exception("Principal databases must implement the PrincipalDatabase interface"); - } - - initialisePrincipalDatabase((PrincipalDatabase) o, _configuration, i); - - String name = databaseNames.get(i); - if ((name == null) || (name.length() == 0)) - { - throw new Exception("Principal database names must have length greater than or equal to one character"); - } - - PrincipalDatabase pd = databases.get(name); - if (pd != null) - { - throw new Exception("Duplicate principal database name not permitted"); - } - - _logger.info("Initialised principal database '" + name + "' successfully"); - databases.put(name, (PrincipalDatabase) o); - } - - return databases; - } - - private void initialisePrincipalDatabase(PrincipalDatabase principalDatabase, ServerConfiguration _configuration, int index) - throws FileNotFoundException, ConfigurationException - { - List<String> argumentNames = _configuration.getPrincipalDatabaseAttributeNames(index); - List<String> argumentValues = _configuration.getPrincipalDatabaseAttributeValues(index); - for (int i = 0; i < argumentNames.size(); i++) - { - String argName = argumentNames.get(i); - if ((argName == null) || (argName.length() == 0)) - { - throw new ConfigurationException("Argument names must have length >= 1 character"); - } - - if (Character.isLowerCase(argName.charAt(0))) - { - argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1); - } - - String methodName = "set" + argName; - Method method = null; - try - { - method = principalDatabase.getClass().getMethod(methodName, String.class); - } - catch (Exception e) - { - // do nothing.. as on error method will be null - } - - if (method == null) - { - throw new ConfigurationException("No method " + methodName + " found in class " - + principalDatabase.getClass() - + " hence unable to configure principal database. The method must be public and " - + "have a single String argument with a void return type"); - } - - try - { - method.invoke(principalDatabase, PropertyUtils.replaceProperties(argumentValues.get(i))); - } - catch (Exception ite) - { - if (ite instanceof ConfigurationException) - { - throw(ConfigurationException) ite; - } - else - { - throw new ConfigurationException(ite.getMessage(), ite); - } - } - } - } - - public Map<String, PrincipalDatabase> getDatabases() - { - return _databases; - } - - public void initialiseManagement(ServerConfiguration config) throws ConfigurationException - { - try - { - AMQUserManagementMBean _mbean = new AMQUserManagementMBean(); - - List<String> principalDBs = config.getManagementPrincipalDBs(); - if (principalDBs.isEmpty()) - { - throw new ConfigurationException("No principal-database specified for jmx security"); - } - - String databaseName = principalDBs.get(0); - PrincipalDatabase database = getDatabases().get(databaseName); - if (database == null) - { - throw new ConfigurationException("Principal-database '" + databaseName + "' not found"); - } - - _mbean.setPrincipalDatabase(database); - - List<String> jmxaccesslist = config.getManagementAccessList(); - if (jmxaccesslist.isEmpty()) - { - throw new ConfigurationException("No access control files specified for jmx security"); - } - - String jmxaccesssFile = null; - - try - { - jmxaccesssFile = PropertyUtils.replaceProperties(jmxaccesslist.get(0)); - } - catch (PropertyException e) - { - throw new ConfigurationException("Unable to parse access control filename '" + jmxaccesssFile + "'"); - } - - try - { - _mbean.setAccessFile(jmxaccesssFile); - } - catch (IOException e) - { - _logger.warn("Unable to load access file:" + jmxaccesssFile); - } - - _mbean.register(); - } - catch (JMException e) - { - _logger.warn("User management disabled as unable to create MBean:" + e); - } - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java deleted file mode 100644 index f9882f8810..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PrincipalDatabaseManager.java +++ /dev/null @@ -1,35 +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.auth.database; - -import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.security.auth.database.PrincipalDatabase; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; - -import java.util.Map; - -public interface PrincipalDatabaseManager -{ - public Map<String, PrincipalDatabase> getDatabases(); - - public void initialiseManagement(ServerConfiguration _configuration) throws ConfigurationException; -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java deleted file mode 100644 index 8658101cd8..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabaseManager.java +++ /dev/null @@ -1,49 +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.auth.database; - -import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.configuration.ServerConfiguration; - -import java.util.Map; -import java.util.Properties; -import java.util.HashMap; - -public class PropertiesPrincipalDatabaseManager implements PrincipalDatabaseManager -{ - - Map<String, PrincipalDatabase> _databases = new HashMap<String, PrincipalDatabase>(); - - public PropertiesPrincipalDatabaseManager(String name, Properties users) - { - _databases.put(name, new PropertiesPrincipalDatabase(users)); - } - - public Map<String, PrincipalDatabase> getDatabases() - { - return _databases; - } - - public void initialiseManagement(ServerConfiguration _configuration) throws ConfigurationException - { - //todo - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java index ee4336055b..208130379e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/management/AMQUserManagementMBean.java @@ -20,19 +20,9 @@ */ package org.apache.qpid.server.security.auth.management; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.security.AccessControlContext; -import java.security.AccessController; import java.security.Principal; -import java.util.Enumeration; import java.util.List; -import java.util.Properties; -import java.util.Random; -import java.util.Set; -import java.util.concurrent.locks.ReentrantLock; import javax.management.JMException; import javax.management.openmbean.CompositeData; @@ -44,17 +34,13 @@ import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; -import javax.management.remote.JMXPrincipal; -import javax.security.auth.Subject; import javax.security.auth.login.AccountNotFoundException; -import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.apache.qpid.management.common.mbeans.UserManagement; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; import org.apache.qpid.server.management.AMQManagedObject; -import org.apache.qpid.server.management.MBeanInvocationHandlerImpl; import org.apache.qpid.server.security.auth.database.PrincipalDatabase; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; @@ -65,22 +51,18 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana private static final Logger _logger = Logger.getLogger(AMQUserManagementMBean.class); private PrincipalDatabase _principalDatabase; - private Properties _accessRights; - private File _accessFile; - - private ReentrantLock _accessRightsUpdate = new ReentrantLock(); // Setup for the TabularType - static TabularType _userlistDataType; // Datatype for representing User Lists - static CompositeType _userDataType; // Composite type for representing User + private static final TabularType _userlistDataType; // Datatype for representing User Lists + private static final CompositeType _userDataType; // Composite type for representing User static { OpenType[] userItemTypes = new OpenType[4]; // User item types. userItemTypes[0] = SimpleType.STRING; // For Username - userItemTypes[1] = SimpleType.BOOLEAN; // For Rights - Read - userItemTypes[2] = SimpleType.BOOLEAN; // For Rights - Write - userItemTypes[3] = SimpleType.BOOLEAN; // For Rights - Admin + userItemTypes[1] = SimpleType.BOOLEAN; // For Rights - Read - No longer in use + userItemTypes[2] = SimpleType.BOOLEAN; // For Rights - Write - No longer in use + userItemTypes[3] = SimpleType.BOOLEAN; // For Rights - Admin - No longer is use try { @@ -92,12 +74,11 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana } catch (OpenDataException e) { - _logger.error("Tabular data setup for viewing users incorrect."); - _userlistDataType = null; + _logger.error("Tabular data setup for viewing users incorrect.", e); + throw new ExceptionInInitializerError("Tabular data setup for viewing users incorrect"); } } - public AMQUserManagementMBean() throws JMException { super(UserManagement.class, UserManagement.TYPE); @@ -110,121 +91,23 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana public boolean setPassword(String username, String password) { - return setPassword(username, password.toCharArray()); - } - - public boolean setPassword(String username, char[] password) - { try { //delegate password changes to the Principal Database - return _principalDatabase.updatePassword(new UsernamePrincipal(username), password); + return _principalDatabase.updatePassword(new UsernamePrincipal(username), password.toCharArray()); } catch (AccountNotFoundException e) { - _logger.warn("Attempt to set password of non-existant user'" + username + "'"); + _logger.warn("Attempt to set password of non-existent user'" + username + "'"); return false; } } - public boolean setRights(String username, boolean read, boolean write, boolean admin) - { - - Object oldRights = null; - if ((oldRights =_accessRights.get(username)) == null) - { - // If the user doesn't exist in the access rights file check that they at least have an account. - if (_principalDatabase.getUser(username) == null) - { - return false; - } - } - - try - { - _accessRightsUpdate.lock(); - - // Update the access rights - if (admin) - { - _accessRights.put(username, MBeanInvocationHandlerImpl.ADMIN); - } - else - { - if (read | write) - { - if (read) - { - _accessRights.put(username, MBeanInvocationHandlerImpl.READONLY); - } - if (write) - { - _accessRights.put(username, MBeanInvocationHandlerImpl.READWRITE); - } - } - else - { - _accessRights.remove(username); - } - } - - //save the rights file - try - { - saveAccessFile(); - } - catch (IOException e) - { - _logger.warn("Problem occured saving '" + _accessFile + "', the access right changes will not be preserved: " + e); - - //the rights file was not successfully saved, restore user rights to previous value - _logger.warn("Reverting attempted rights update for user'" + username + "'"); - if (oldRights != null) - { - _accessRights.put(username, oldRights); - } - else - { - _accessRights.remove(username); - } - - return false; - } - } - finally - { - _accessRightsUpdate.unlock(); - } - - return true; - } - - public boolean createUser(String username, String password, boolean read, boolean write, boolean admin) - { - return createUser(username, password.toCharArray(), read, write, admin); - } - - public boolean createUser(String username, char[] password, boolean read, boolean write, boolean admin) + public boolean createUser(String username, String password) { - if (_principalDatabase.createPrincipal(new UsernamePrincipal(username), password)) + if (_principalDatabase.createPrincipal(new UsernamePrincipal(username), password.toCharArray())) { - if (!setRights(username, read, write, admin)) - { - //unable to set rights for user, remove account - try - { - _principalDatabase.deletePrincipal(new UsernamePrincipal(username)); - } - catch (AccountNotFoundException e) - { - //ignore - } - return false; - } - else - { - return true; - } + return true; } return false; @@ -234,29 +117,7 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana { try { - if (_principalDatabase.deletePrincipal(new UsernamePrincipal(username))) - { - try - { - _accessRightsUpdate.lock(); - - _accessRights.remove(username); - - try - { - saveAccessFile(); - } - catch (IOException e) - { - _logger.warn("Problem occured saving '" + _accessFile + "', the access right changes will not be preserved: " + e); - return false; - } - } - finally - { - _accessRightsUpdate.unlock(); - } - } + _principalDatabase.deletePrincipal(new UsernamePrincipal(username)); } catch (AccountNotFoundException e) { @@ -269,38 +130,23 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana public boolean reloadData() { - try - { - loadAccessFile(); - _principalDatabase.reload(); - } - catch (ConfigurationException e) - { - _logger.warn("Reload failed due to:" + e); - return false; - } - catch (IOException e) - { - _logger.warn("Reload failed due to:" + e); - return false; - } - // Reload successful - return true; + try + { + _principalDatabase.reload(); + } + catch (IOException e) + { + _logger.warn("Reload failed due to:", e); + return false; + } + // Reload successful + return true; } - @MBeanOperation(name = "viewUsers", description = "All users with access rights to the system.") + @MBeanOperation(name = "viewUsers", description = "All users that are currently available to the system.") public TabularData viewUsers() { - // Table of users - // Username(string), Access rights Read,Write,Admin(bool,bool,bool) - - if (_userlistDataType == null) - { - _logger.warn("TabluarData not setup correctly"); - return null; - } - List<Principal> users = _principalDatabase.getUsers(); TabularDataSupport userList = new TabularDataSupport(_userlistDataType); @@ -311,29 +157,15 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana for (Principal user : users) { // Create header attributes list - - String rights = (String) _accessRights.get(user.getName()); - - Boolean read = false; - Boolean write = false; - Boolean admin = false; - - if (rights != null) - { - read = rights.equals(MBeanInvocationHandlerImpl.READONLY) - || rights.equals(MBeanInvocationHandlerImpl.READWRITE); - write = rights.equals(MBeanInvocationHandlerImpl.READWRITE); - admin = rights.equals(MBeanInvocationHandlerImpl.ADMIN); - } - - Object[] itemData = {user.getName(), read, write, admin}; + // Read,Write,Admin items are depcreated and we return always false. + Object[] itemData = {user.getName(), false, false, false}; CompositeData messageData = new CompositeDataSupport(_userDataType, COMPOSITE_ITEM_NAMES.toArray(new String[COMPOSITE_ITEM_NAMES.size()]), itemData); userList.put(messageData); } } catch (OpenDataException e) { - _logger.warn("Unable to create user list due to :" + e); + _logger.warn("Unable to create user list due to :", e); return null; } @@ -351,187 +183,4 @@ public class AMQUserManagementMBean extends AMQManagedObject implements UserMana { _principalDatabase = database; } - - /** - * setAccessFile - * - * @param accessFile the file to use for updating. - * - * @throws java.io.IOException If the file cannot be accessed - * @throws org.apache.commons.configuration.ConfigurationException - * if checks on the file fail. - */ - public void setAccessFile(String accessFile) throws IOException, ConfigurationException - { - if (accessFile != null) - { - _accessFile = new File(accessFile); - if (!_accessFile.exists()) - { - throw new ConfigurationException("'" + _accessFile + "' does not exist"); - } - - if (!_accessFile.canRead()) - { - throw new ConfigurationException("Cannot read '" + _accessFile + "'."); - } - - if (!_accessFile.canWrite()) - { - _logger.warn("Unable to write to access rights file '" + _accessFile + "', changes will not be preserved."); - } - - loadAccessFile(); - } - else - { - _logger.warn("Access rights file specified is null. Access rights not changed."); - } - } - - private void loadAccessFile() throws IOException, ConfigurationException - { - if(_accessFile == null) - { - _logger.error("No jmx access rights file has been specified."); - return; - } - - if(_accessFile.exists()) - { - try - { - _accessRightsUpdate.lock(); - - Properties accessRights = new Properties(); - FileInputStream inStream = new FileInputStream(_accessFile); - try - { - accessRights.load(inStream); - } - finally - { - inStream.close(); - } - - checkAccessRights(accessRights); - setAccessRights(accessRights); - } - finally - { - _accessRightsUpdate.unlock(); - } - } - else - { - _logger.error("Specified jmxaccess rights file '" + _accessFile + "' does not exist."); - } - } - - private void checkAccessRights(Properties accessRights) - { - Enumeration values = accessRights.propertyNames(); - - while (values.hasMoreElements()) - { - String user = (String) values.nextElement(); - - if (_principalDatabase.getUser(user) == null) - { - _logger.warn("Access rights contains user '" + user + "' but there is no authentication data for that user"); - } - } - } - - private void saveAccessFile() throws IOException - { - try - { - _accessRightsUpdate.lock(); - - // Create temporary file - Random r = new Random(); - File tmp; - do - { - tmp = new File(_accessFile.getPath() + r.nextInt() + ".tmp"); - } - while(tmp.exists()); - - tmp.deleteOnExit(); - - FileOutputStream output = new FileOutputStream(tmp); - _accessRights.store(output, "Generated by AMQUserManagementMBean Console : Last edited by user:" + getCurrentJMXUser()); - output.close(); - - // Swap temp file to main rights file. - File old = new File(_accessFile.getAbsoluteFile() + ".old"); - if (old.exists()) - { - old.delete(); - } - - if(!_accessFile.renameTo(old)) - { - //unable to rename the existing file to the backup name - _logger.error("Could not backup the existing management rights file"); - throw new IOException("Could not backup the existing management rights file"); - } - - if(!tmp.renameTo(_accessFile)) - { - //failed to rename the new file to the required filename - - if(!old.renameTo(_accessFile)) - { - //unable to return the backup to required filename - _logger.error("Could not rename the new management rights file into place, and unable to restore original file"); - throw new IOException("Could not rename the new management rights file into place, and unable to restore original file"); - } - - _logger.error("Could not rename the new management rights file into place"); - throw new IOException("Could not rename the new management rights file into place"); - } - } - finally - { - _accessRightsUpdate.unlock(); - } - - } - - private String getCurrentJMXUser() - { - AccessControlContext acc = AccessController.getContext(); - - Subject subject = Subject.getSubject(acc); - if (subject == null) - { - return "Unknown user, authentication Subject was null"; - } - - // Retrieve JMXPrincipal from Subject - Set<JMXPrincipal> principals = subject.getPrincipals(JMXPrincipal.class); - if (principals == null || principals.isEmpty()) - { - return "Unknown user principals were null"; - } - - Principal principal = principals.iterator().next(); - return principal.getName(); - } - - /** - * user=read user=write user=readwrite user=admin - * - * @param accessRights The properties list of access rights to process - */ - private void setAccessRights(Properties accessRights) - { - _logger.debug("Setting Access Rights:" + accessRights); - _accessRights = accessRights; - - // TODO check where this is used - // MBeanInvocationHandlerImpl.setAccessRights(_accessRights); - } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java index bc771162fd..4c59c25d84 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java @@ -20,17 +20,73 @@ */ package org.apache.qpid.server.security.auth.manager; +import javax.security.auth.Subject; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.qpid.common.Closeable; +import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.security.auth.AuthenticationResult; -public interface AuthenticationManager extends Closeable +/** + * Implementations of the AuthenticationManager are responsible for determining + * the authenticity of a user's credentials. + * + * If the authentication is successful, the manager is responsible for producing a populated + * {@link Subject} containing the user's identity and zero or more principals representing + * groups to which the user belongs. + * <p> + * The {@link #initialise()} method is responsible for registering SASL mechanisms required by + * the manager. The {@link #close()} method must reverse this registration. + * + */ +public interface AuthenticationManager extends Closeable, Plugin { + /** The name for the required SASL Server mechanisms */ + public static final String PROVIDER_NAME= "AMQSASLProvider-Server"; + + /** + * Initialise the authentication plugin. + * + */ + void initialise(); + + /** + * Gets the SASL mechanisms known to this manager. + * + * @return SASL mechanism names, space separated. + */ String getMechanisms(); + /** + * Creates a SASL server for the specified mechanism name for the given + * fully qualified domain name. + * + * @param mechanism mechanism name + * @param localFQDN domain name + * + * @return SASL server + * @throws SaslException + */ SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException; + /** + * Authenticates a user using SASL negotiation. + * + * @param server SASL server + * @param response SASL response to process + * + * @return authentication result + */ AuthenticationResult authenticate(SaslServer server, byte[] response); + + /** + * Authenticates a user using their username and password. + * + * @param username username + * @param password password + * + * @return authentication result + */ + AuthenticationResult authenticate(String username, String password); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerPluginFactory.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerPluginFactory.java new file mode 100644 index 0000000000..a51f195761 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerPluginFactory.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.server.security.auth.manager; + +import org.apache.qpid.server.plugins.PluginFactory; + +/** + * Factory producing authentication producing configured, initialised authentication + * managers. + */ +public interface AuthenticationManagerPluginFactory<S extends AuthenticationManager> extends PluginFactory<S> +{ + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java index 2a967f02af..1945c2e15f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java @@ -20,27 +20,65 @@ */ package org.apache.qpid.server.security.auth.manager; -import org.apache.log4j.Logger; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.Security; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.AccountNotFoundException; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.security.sasl.SaslServerFactory; + import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.configuration.VirtualHostConfiguration; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.log4j.Logger; +import org.apache.qpid.configuration.PropertyException; +import org.apache.qpid.configuration.PropertyUtils; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.security.auth.database.PrincipalDatabase; -import org.apache.qpid.server.security.auth.sasl.JCAProvider; +import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean; import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; -import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.sasl.JCAProvider; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; -import javax.security.auth.callback.CallbackHandler; -import javax.security.sasl.SaslServerFactory; -import javax.security.sasl.SaslServer; -import javax.security.sasl.SaslException; -import javax.security.sasl.Sasl; -import java.util.Map; -import java.util.HashMap; -import java.util.TreeMap; -import java.security.Security; +/** + * Concrete implementation of the AuthenticationManager that determines if supplied + * user credentials match those appearing in a PrincipalDatabase. The implementation + * of the PrincipalDatabase is determined from the configuration. + * + * This implementation also registers the JMX UserManagemement MBean. + * + * This plugin expects configuration such as: + * + * <pre> + * <pd-auth-manager> + * <principal-database> + * <class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class> + * <attributes> + * <attribute> + * <name>passwordFile</name> + * <value>${conf}/passwd</value> + * </attribute> + * </attributes> + * </principal-database> + * </pd-auth-manager> + * </pre> + */ public class PrincipalDatabaseAuthenticationManager implements AuthenticationManager { private static final Logger _logger = Logger.getLogger(PrincipalDatabaseAuthenticationManager.class); @@ -49,55 +87,109 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan private String _mechanisms; /** Maps from the mechanism to the callback handler to use for handling those requests */ - private Map<String, CallbackHandler> _callbackHandlerMap = new HashMap<String, CallbackHandler>(); + private final Map<String, CallbackHandler> _callbackHandlerMap = new HashMap<String, CallbackHandler>(); /** * Maps from the mechanism to the properties used to initialise the server. See the method Sasl.createSaslServer for * details of the use of these properties. This map is populated during initialisation of each provider. */ - private Map<String, Map<String, ?>> _serverCreationProperties = new HashMap<String, Map<String, ?>>(); + private final Map<String, Map<String, ?>> _serverCreationProperties = new HashMap<String, Map<String, ?>>(); - private AuthenticationManager _default = null; - /** The name for the required SASL Server mechanisms */ - public static final String PROVIDER_NAME= "AMQSASLProvider-Server"; + protected PrincipalDatabase _principalDatabase = null; - public PrincipalDatabaseAuthenticationManager(String name, VirtualHostConfiguration hostConfig) throws Exception - { - _logger.info("Initialising " + (name == null ? "Default" : "'" + name + "'") - + " PrincipalDatabase authentication manager."); + protected AMQUserManagementMBean _mbean = null; - // Fixme This should be done per Vhost but allowing global hack isn't right but ... - // required as authentication is done before Vhost selection + public static final AuthenticationManagerPluginFactory<PrincipalDatabaseAuthenticationManager> FACTORY = new AuthenticationManagerPluginFactory<PrincipalDatabaseAuthenticationManager>() + { + public PrincipalDatabaseAuthenticationManager newInstance(final ConfigurationPlugin config) throws ConfigurationException + { + final PrincipalDatabaseAuthenticationManagerConfiguration configuration = config.getConfiguration(PrincipalDatabaseAuthenticationManagerConfiguration.class.getName()); - Map<String, Class<? extends SaslServerFactory>> providerMap = new TreeMap<String, Class<? extends SaslServerFactory>>(); + // If there is no configuration for this plugin then don't load it. + if (configuration == null) + { + _logger.info("No authentication-manager configuration found for PrincipalDatabaseAuthenticationManager"); + return null; + } + final PrincipalDatabaseAuthenticationManager pdam = new PrincipalDatabaseAuthenticationManager(); + pdam.configure(configuration); + pdam.initialise(); + return pdam; + } - if (name == null || hostConfig == null) + public Class<PrincipalDatabaseAuthenticationManager> getPluginClass() { - initialiseAuthenticationMechanisms(providerMap, ApplicationRegistry.getInstance().getDatabaseManager().getDatabases()); + return PrincipalDatabaseAuthenticationManager.class; } - else + + public String getPluginName() { - String databaseName = hostConfig.getAuthenticationDatabase(); + return PrincipalDatabaseAuthenticationManager.class.getName(); + } + }; - if (databaseName == null) + public static class PrincipalDatabaseAuthenticationManagerConfiguration extends ConfigurationPlugin { + + public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() + { + public List<String> getParentPaths() { - - _default = ApplicationRegistry.getInstance().getAuthenticationManager(); - return; + return Arrays.asList("security.pd-auth-manager"); } - else + + public ConfigurationPlugin newInstance(final String path, final Configuration config) throws ConfigurationException { - PrincipalDatabase database = ApplicationRegistry.getInstance().getDatabaseManager().getDatabases().get(databaseName); + final ConfigurationPlugin instance = new PrincipalDatabaseAuthenticationManagerConfiguration(); + + instance.setConfiguration(path, config); + return instance; + } + }; - if (database == null) - { - throw new ConfigurationException("Requested database:" + databaseName + " was not found"); - } + public String[] getElementsProcessed() + { + return new String[] {"principal-database.class", + "principal-database.attributes.attribute.name", + "principal-database.attributes.attribute.value"}; + } - initialiseAuthenticationMechanisms(providerMap, database); + public void validateConfiguration() throws ConfigurationException + { + } + + public String getPrincipalDatabaseClass() + { + return _configuration.getString("principal-database.class"); + } + + public Map<String,String> getPdClassAttributeMap() throws ConfigurationException + { + final List<String> argumentNames = _configuration.getList("principal-database.attributes.attribute.name"); + final List<String> argumentValues = _configuration.getList("principal-database.attributes.attribute.value"); + final Map<String,String> attributes = new HashMap<String,String>(argumentNames.size()); + + for (int i = 0; i < argumentNames.size(); i++) + { + final String argName = argumentNames.get(i); + final String argValue = argumentValues.get(i); + + attributes.put(argName, argValue); } + + return Collections.unmodifiableMap(attributes); } + } + + protected PrincipalDatabaseAuthenticationManager() + { + } + + public void initialise() + { + final Map<String, Class<? extends SaslServerFactory>> providerMap = new TreeMap<String, Class<? extends SaslServerFactory>>(); + + initialiseAuthenticationMechanisms(providerMap, _principalDatabase); if (providerMap.size() > 0) { @@ -110,33 +202,16 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan { _logger.info("Additional SASL providers successfully registered."); } - } else { _logger.warn("No additional SASL providers registered."); } + registerManagement(); } - - private void initialiseAuthenticationMechanisms(Map<String, Class<? extends SaslServerFactory>> providerMap, Map<String, PrincipalDatabase> databases) throws Exception - { - if (databases.size() > 1) - { - _logger.warn("More than one principle database provided currently authentication mechanism will override each other."); - } - - for (Map.Entry<String, PrincipalDatabase> entry : databases.entrySet()) - { - // fixme As the database now provide the mechanisms they support, they will ... - // overwrite each other in the map. There should only be one database per vhost. - // But currently we must have authentication before vhost definition. - initialiseAuthenticationMechanisms(providerMap, entry.getValue()); - } - } - - private void initialiseAuthenticationMechanisms(Map<String, Class<? extends SaslServerFactory>> providerMap, PrincipalDatabase database) throws Exception + private void initialiseAuthenticationMechanisms(Map<String, Class<? extends SaslServerFactory>> providerMap, PrincipalDatabase database) { if (database == null || database.getMechanisms().size() == 0) { @@ -152,7 +227,6 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan private void initialiseAuthenticationMechanism(String mechanism, AuthenticationProviderInitialiser initialiser, Map<String, Class<? extends SaslServerFactory>> providerMap) - throws Exception { if (_mechanisms == null) { @@ -173,43 +247,37 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan _logger.info("Initialised " + mechanism + " SASL provider successfully"); } + /** + * @see org.apache.qpid.server.plugins.Plugin#configure(org.apache.qpid.server.configuration.plugins.ConfigurationPlugin) + */ + public void configure(final ConfigurationPlugin config) throws ConfigurationException + { + final PrincipalDatabaseAuthenticationManagerConfiguration pdamConfig = (PrincipalDatabaseAuthenticationManagerConfiguration) config; + final String pdClazz = pdamConfig.getPrincipalDatabaseClass(); + + _logger.info("PrincipalDatabase concrete implementation : " + pdClazz); + + _principalDatabase = createPrincipalDatabaseImpl(pdClazz); + + configPrincipalDatabase(_principalDatabase, pdamConfig); + } + public String getMechanisms() { - if (_default != null) - { - // Use the default AuthenticationManager if present - return _default.getMechanisms(); - } - else - { - return _mechanisms; - } + return _mechanisms; } public SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException { - if (_default != null) - { - // Use the default AuthenticationManager if present - return _default.createSaslServer(mechanism, localFQDN); - } - else - { - return Sasl.createSaslServer(mechanism, "AMQP", localFQDN, _serverCreationProperties.get(mechanism), - _callbackHandlerMap.get(mechanism)); - } - + return Sasl.createSaslServer(mechanism, "AMQP", localFQDN, _serverCreationProperties.get(mechanism), + _callbackHandlerMap.get(mechanism)); } + /** + * @see org.apache.qpid.server.security.auth.manager.AuthenticationManager#authenticate(SaslServer, byte[]) + */ public AuthenticationResult authenticate(SaslServer server, byte[] response) { - // Use the default AuthenticationManager if present - if (_default != null) - { - return _default.authenticate(server, response); - } - - try { // Process response from the client @@ -217,7 +285,9 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan if (server.isComplete()) { - return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.SUCCESS); + final Subject subject = new Subject(); + subject.getPrincipals().add(new UsernamePrincipal(server.getAuthorizationID())); + return new AuthenticationResult(subject); } else { @@ -230,8 +300,164 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan } } + /** + * @see org.apache.qpid.server.security.auth.manager.AuthenticationManager#authenticate(String, String) + */ + public AuthenticationResult authenticate(final String username, final String password) + { + try + { + if (_principalDatabase.verifyPassword(username, password.toCharArray())) + { + final Subject subject = new Subject(); + subject.getPrincipals().add(new UsernamePrincipal(username)); + return new AuthenticationResult(subject); + } + else + { + return new AuthenticationResult(AuthenticationStatus.CONTINUE); + } + } + catch (AccountNotFoundException e) + { + return new AuthenticationResult(AuthenticationStatus.CONTINUE); + } + } + public void close() { + _mechanisms = null; Security.removeProvider(PROVIDER_NAME); + + unregisterManagement(); + } + + private PrincipalDatabase createPrincipalDatabaseImpl(final String pdClazz) throws ConfigurationException + { + try + { + return (PrincipalDatabase) Class.forName(pdClazz).newInstance(); + } + catch (InstantiationException ie) + { + throw new ConfigurationException("Cannot instantiate " + pdClazz, ie); + } + catch (IllegalAccessException iae) + { + throw new ConfigurationException("Cannot access " + pdClazz, iae); + } + catch (ClassNotFoundException cnfe) + { + throw new ConfigurationException("Cannot load " + pdClazz + " implementation", cnfe); + } + catch (ClassCastException cce) + { + throw new ConfigurationException("Expecting a " + PrincipalDatabase.class + " implementation", cce); + } + } + + private void configPrincipalDatabase(final PrincipalDatabase principalDatabase, final PrincipalDatabaseAuthenticationManagerConfiguration config) + throws ConfigurationException + { + + final Map<String,String> attributes = config.getPdClassAttributeMap(); + + for (Iterator<Entry<String, String>> iterator = attributes.entrySet().iterator(); iterator.hasNext();) + { + final Entry<String, String> nameValuePair = iterator.next(); + final String methodName = generateSetterName(nameValuePair.getKey()); + final Method method; + try + { + method = principalDatabase.getClass().getMethod(methodName, String.class); + } + catch (Exception e) + { + throw new ConfigurationException("No method " + methodName + " found in class " + + principalDatabase.getClass() + + " hence unable to configure principal database. The method must be public and " + + "have a single String argument with a void return type", e); + } + try + { + method.invoke(principalDatabase, PropertyUtils.replaceProperties(nameValuePair.getValue())); + } + catch (IllegalArgumentException e) + { + throw new ConfigurationException(e.getMessage(), e); + } + catch (PropertyException e) + { + throw new ConfigurationException(e.getMessage(), e); + } + catch (IllegalAccessException e) + { + throw new ConfigurationException(e.getMessage(), e); + } + catch (InvocationTargetException e) + { + // QPID-1347.. InvocationTargetException wraps the checked exception thrown from the reflective + // method call. Pull out the underlying message and cause to make these more apparent to the user. + throw new ConfigurationException(e.getCause().getMessage(), e.getCause()); + } + } + } + + private String generateSetterName(String argName) throws ConfigurationException + { + if ((argName == null) || (argName.length() == 0)) + { + throw new ConfigurationException("Argument names must have length >= 1 character"); + } + + if (Character.isLowerCase(argName.charAt(0))) + { + argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1); + } + + final String methodName = "set" + argName; + return methodName; + } + + protected void setPrincipalDatabase(final PrincipalDatabase principalDatabase) + { + _principalDatabase = principalDatabase; + } + + protected void registerManagement() + { + try + { + _logger.info("Registering UserManagementMBean"); + + _mbean = new AMQUserManagementMBean(); + _mbean.setPrincipalDatabase(_principalDatabase); + _mbean.register(); + } + catch (Exception e) + { + _logger.warn("User management disabled as unable to create MBean:", e); + _mbean = null; + } + } + + protected void unregisterManagement() + { + try + { + if (_mbean != null) + { + _logger.info("Unregistering UserManagementMBean"); + _mbean.unregister(); + } + } + catch (Exception e) + { + _logger.warn("Failed to unregister User management MBean:", e); + } + finally + { + _mbean = null; + } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java index 0cbbccb3b8..b7985ad972 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java @@ -20,14 +20,13 @@ */ package org.apache.qpid.server.security.auth.rmi; -import java.util.Collections; - import javax.management.remote.JMXAuthenticator; import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; -import javax.security.auth.login.AccountNotFoundException; -import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; public class RMIPasswordAuthenticator implements JMXAuthenticator { @@ -39,15 +38,15 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator static final String CREDENTIALS_REQUIRED = "User details are required. " + "Please ensure you are using an up to date management console to connect."; - private PrincipalDatabase _db = null; + private AuthenticationManager _authenticationManager = null; public RMIPasswordAuthenticator() { } - - public void setPrincipalDatabase(PrincipalDatabase pd) + + public void setAuthenticationManager(final AuthenticationManager authenticationManager) { - this._db = pd; + _authenticationManager = authenticationManager; } public Subject authenticate(Object credentials) throws SecurityException @@ -65,50 +64,39 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator } } - // Verify that required number of credential's. + // Verify that required number of credentials. final String[] userCredentials = (String[]) credentials; if (userCredentials.length != 2) { throw new SecurityException(SHOULD_HAVE_2_ELEMENTS); } - String username = (String) userCredentials[0]; - String password = (String) userCredentials[1]; + final String username = (String) userCredentials[0]; + final String password = (String) userCredentials[1]; - // Verify that all required credential's are actually present. + // Verify that all required credentials are actually present. if (username == null || password == null) { throw new SecurityException(SHOULD_BE_NON_NULL); } - // Verify that a PD has been set. - if (_db == null) + // Verify that an AuthenticationManager has been set. + if (_authenticationManager == null) { throw new SecurityException(UNABLE_TO_LOOKUP); } - - boolean authenticated = false; + final AuthenticationResult result = _authenticationManager.authenticate(username, password); - // Perform authentication - try + if (AuthenticationStatus.ERROR.equals(result.getStatus())) { - if (_db.verifyPassword(username, password.toCharArray())) - { - authenticated = true; - } - } - catch (AccountNotFoundException e) - { - throw new SecurityException(INVALID_CREDENTIALS); // XXX + throw new SecurityException("Authentication manager failed", result.getCause()); } - - if (authenticated) + else if (AuthenticationStatus.SUCCESS.equals(result.getStatus())) { - //credential's check out, return the appropriate JAAS Subject - return new Subject(true, - Collections.singleton(new JMXPrincipal(username)), - Collections.EMPTY_SET, - Collections.EMPTY_SET); + final Subject subject = result.getSubject(); + subject.getPrincipals().add(new JMXPrincipal(username)); + subject.setReadOnly(); + return subject; } else { diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java index 89e545d6f5..bc5d8a4f2b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/AuthenticationProviderInitialiser.java @@ -25,9 +25,6 @@ import java.util.Map; import javax.security.auth.callback.CallbackHandler; import javax.security.sasl.SaslServerFactory; -import org.apache.commons.configuration.Configuration; -import org.apache.qpid.server.security.auth.database.PrincipalDatabase; - public interface AuthenticationProviderInitialiser { /** @@ -37,24 +34,6 @@ public interface AuthenticationProviderInitialiser String getMechanismName(); /** - * Initialise the authentication provider. - * @param baseConfigPath the path in the config file that points to any config options for this provider. Each - * provider can have its own set of configuration options - * @param configuration the Apache Commons Configuration instance used to configure this provider - * @param principalDatabases the set of principal databases that are available - * @throws Exception needs refined Exception is too broad. - */ - void initialise(String baseConfigPath, Configuration configuration, - Map<String, PrincipalDatabase> principalDatabases) throws Exception; - - /** - * Initialise the authentication provider. - * @param db The principal database to initialise with - */ - void initialise(PrincipalDatabase db); - - - /** * @return the callback handler that should be used to process authentication requests for this mechanism. This will * be called after initialise and will be stored by the authentication manager. The callback handler <b>must</b> be * fully threadsafe. diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipal.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipal.java new file mode 100644 index 0000000000..30a503c769 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipal.java @@ -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. + * + */ +package org.apache.qpid.server.security.auth.sasl; + +import java.security.Principal; +import java.security.acl.Group; +import java.util.Enumeration; + +/** + * Immutable representation of a user group. In Qpid, groups do <b>not</b> know + * about their membership, and therefore the {@link #addMember(Principal)} + * methods etc throw {@link UnsupportedOperationException}. + * + */ +public class GroupPrincipal implements Group +{ + /** Name of the group */ + private final String _groupName; + + public GroupPrincipal(final String groupName) + { + _groupName = groupName; + } + + public String getName() + { + return _groupName; + } + + public boolean addMember(Principal user) + { + throw new UnsupportedOperationException("Not supported"); + } + + public boolean removeMember(Principal user) + { + throw new UnsupportedOperationException("Not supported"); + } + + public boolean isMember(Principal member) + { + throw new UnsupportedOperationException("Not supported"); + } + + public Enumeration<? extends Principal> members() + { + throw new UnsupportedOperationException("Not supported"); + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + final int prime = 37; + return prime * _groupName.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else + { + if (obj instanceof GroupPrincipal) + { + GroupPrincipal other = (GroupPrincipal) obj; + return _groupName.equals(other._groupName); + } + else + { + return false; + } + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java index d6a09d8217..d6f6c714e2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/JCAProvider.java @@ -21,12 +21,11 @@ package org.apache.qpid.server.security.auth.sasl; import java.security.Provider; -import java.security.Security; import java.util.Map; import javax.security.sasl.SaslServerFactory; -public final class JCAProvider extends Provider +public class JCAProvider extends Provider { public JCAProvider(String name, Map<String, Class<? extends SaslServerFactory>> providerMap) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java index d7c8383690..b4ee13fe6b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java @@ -21,14 +21,21 @@ package org.apache.qpid.server.security.auth.sasl; import java.security.Principal; +import java.util.Set; + +import javax.security.auth.Subject; /** A principal that is just a wrapper for a simple username. */ public class UsernamePrincipal implements Principal { - private String _name; + private final String _name; public UsernamePrincipal(String name) { + if (name == null) + { + throw new IllegalArgumentException("name cannot be null"); + } _name = name; } @@ -41,4 +48,53 @@ public class UsernamePrincipal implements Principal { return _name; } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + final int prime = 31; + return prime * _name.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else + { + if (obj instanceof UsernamePrincipal) + { + UsernamePrincipal other = (UsernamePrincipal) obj; + return _name.equals(other._name); + } + else + { + return false; + } + } + } + + public static UsernamePrincipal getUsernamePrincipalFromSubject(final Subject authSubject) + { + if (authSubject == null) + { + throw new IllegalArgumentException("No authenticated subject."); + } + + final Set<UsernamePrincipal> principals = authSubject.getPrincipals(UsernamePrincipal.class); + if (principals.size() != 1) + { + throw new IllegalArgumentException("Can't find single UsernamePrincipal in authenticated subject"); + } + return principals.iterator().next(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java index 9f56b8521a..dee40e7069 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServer.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.security.auth.sasl.amqplain; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.io.IOException; import javax.security.auth.callback.Callback; @@ -31,7 +33,6 @@ import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; -import org.apache.mina.common.ByteBuffer; import org.apache.qpid.framing.AMQFrameDecodingException; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.FieldTableFactory; @@ -60,7 +61,7 @@ public class AmqPlainSaslServer implements SaslServer { try { - final FieldTable ft = FieldTableFactory.newFieldTable(ByteBuffer.wrap(response), response.length); + final FieldTable ft = FieldTableFactory.newFieldTable(new DataInputStream(new ByteArrayInputStream(response)), response.length); String username = (String) ft.getString("LOGIN"); // we do not care about the prompt but it throws if null NameCallback nameCb = new NameCallback("prompt", username); diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java index 67d20136bf..17d123eb0d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/amqplain/AmqPlainSaslServerFactory.java @@ -45,9 +45,10 @@ public class AmqPlainSaslServerFactory implements SaslServerFactory public String[] getMechanismNames(Map props) { - if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || - props.containsKey(Sasl.POLICY_NODICTIONARY) || - props.containsKey(Sasl.POLICY_NOACTIVE)) + if (props != null && + (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || + props.containsKey(Sasl.POLICY_NODICTIONARY) || + props.containsKey(Sasl.POLICY_NOACTIVE))) { // returned array must be non null according to interface documentation return new String[0]; diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServer.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServer.java index b4cce15d88..52d36023c2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServer.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServer.java @@ -20,21 +20,9 @@ */ package org.apache.qpid.server.security.auth.sasl.anonymous; -import java.io.IOException; - -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.sasl.AuthorizeCallback; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; -import org.apache.mina.common.ByteBuffer; -import org.apache.qpid.framing.AMQFrameDecodingException; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.framing.FieldTableFactory; public class AnonymousSaslServer implements SaslServer { diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java index 6032255870..8a5ff7df2d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java @@ -47,10 +47,11 @@ public class AnonymousSaslServerFactory implements SaslServerFactory public String[] getMechanismNames(Map props) { - if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || - props.containsKey(Sasl.POLICY_NODICTIONARY) || - props.containsKey(Sasl.POLICY_NOACTIVE) || - props.containsKey(Sasl.POLICY_NOANONYMOUS)) + if (props != null && + (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || + props.containsKey(Sasl.POLICY_NODICTIONARY) || + props.containsKey(Sasl.POLICY_NOACTIVE) || + props.containsKey(Sasl.POLICY_NOANONYMOUS))) { // returned array must be non null according to interface documentation return new String[0]; diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HexInitialiser.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HexInitialiser.java index 8020d97364..139818735f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HexInitialiser.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/crammd5/CRAMMD5HexInitialiser.java @@ -70,7 +70,7 @@ public class CRAMMD5HexInitialiser extends UsernamePasswordInitialiser for (char c : password) { //toHexString does not prepend 0 so we have to - if (((byte) c > -1) && (byte) c < 10) + if (((byte) c > -1) && (byte) c < 0x10 ) { sb.append(0); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java index f0dd9eeb6d..3144bfbce6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServerFactory.java @@ -45,9 +45,10 @@ public class PlainSaslServerFactory implements SaslServerFactory public String[] getMechanismNames(Map props) { - if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || - props.containsKey(Sasl.POLICY_NODICTIONARY) || - props.containsKey(Sasl.POLICY_NOACTIVE)) + if (props != null && + (props.containsKey(Sasl.POLICY_NOPLAINTEXT) || + props.containsKey(Sasl.POLICY_NODICTIONARY) || + props.containsKey(Sasl.POLICY_NOACTIVE))) { // returned array must be non null according to interface documentation return new String[0]; diff --git a/java/broker/src/main/java/org/apache/qpid/server/signal/SignalHandlerTask.java b/java/broker/src/main/java/org/apache/qpid/server/signal/SignalHandlerTask.java new file mode 100644 index 0000000000..4e3fae1dbd --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/signal/SignalHandlerTask.java @@ -0,0 +1,89 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.signal; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.apache.log4j.Logger; + +public abstract class SignalHandlerTask +{ + private static final Logger LOGGER = Logger.getLogger(SignalHandlerTask.class); + + private static final String HANDLE_METHOD = "handle"; + private static final String SUN_MISC_SIGNAL_CLASS = "sun.misc.Signal"; + private static final String SUN_MISC_SIGNAL_HANDLER_CLASS = "sun.misc.SignalHandler"; + + public boolean register(final String signalName) + { + try + { + //try to load the signal handling classes + Class<?> signalClazz = Class.forName(SUN_MISC_SIGNAL_CLASS); + Class<?> handlerClazz = Class.forName(SUN_MISC_SIGNAL_HANDLER_CLASS); + + //create an InvocationHandler that just executes the SignalHandlerTask + InvocationHandler invoker = new InvocationHandler() + { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + handle(); + + return null; + } + }; + + //create a dynamic proxy implementing SignalHandler + Object handler = Proxy.newProxyInstance(handlerClazz.getClassLoader(), new Class[]{handlerClazz}, invoker); + + //create the Signal to handle + Constructor<?> signalConstructor = signalClazz.getConstructor(String.class); + Object signal = signalConstructor.newInstance(signalName); + + //invoke the Signal.handle(signal, handler) method + Method handleMethod = signalClazz.getMethod(HANDLE_METHOD, signalClazz, handlerClazz); + handleMethod.invoke(null, signal, handler); + } + catch (Exception e) + { + LOGGER.debug("Unable to register handler for Signal " + signalName + " due to exception: " + e, e); + return false; + } + + return true; + } + + public abstract void handle(); + + public static String getPlatformDescription() + { + String name = System.getProperty("os.name"); + String osVer = System.getProperty("os.version"); + String jvmVendor = System.getProperty("java.vm.vendor"); + String jvmName = System.getProperty("java.vm.name"); + String javaRuntimeVer = System.getProperty("java.runtime.version"); + + return "OS: " + name + " " + osVer + ", JVM:" + jvmVendor + " " + jvmName + " " + javaRuntimeVer; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java b/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java index 6cc5e7b019..33aebffcfb 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java @@ -259,7 +259,7 @@ public class AMQStateManager implements AMQMethodListener public AMQProtocolSession getProtocolSession() { - SecurityManager.setThreadPrincipal(_protocolSession.getPrincipal()); + SecurityManager.setThreadSubject(_protocolSession.getAuthorizedSubject()); return _protocolSession; } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java b/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java new file mode 100644 index 0000000000..b732121180 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java @@ -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. + */ +package org.apache.qpid.server.stats; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class collects statistics and counts the total, rate per second and + * peak rate per second values for the events that are registered with it. + */ +public class StatisticsCounter +{ + private static final Logger _log = LoggerFactory.getLogger(StatisticsCounter.class); + + public static final long DEFAULT_SAMPLE_PERIOD = Long.getLong("qpid.statistics.samplePeriod", 2000L); // 2s + public static final boolean DISABLE_STATISTICS = Boolean.getBoolean("qpid.statistics.disable"); + + private static final String COUNTER = "counter"; + private static final AtomicLong _counterIds = new AtomicLong(0L); + + private long _peak = 0L; + private long _total = 0L; + private long _temp = 0L; + private long _last = 0L; + private long _rate = 0L; + + private long _start; + + private final long _period; + private final String _name; + + public StatisticsCounter() + { + this(COUNTER); + } + + public StatisticsCounter(String name) + { + this(name, DEFAULT_SAMPLE_PERIOD); + } + + public StatisticsCounter(String name, long period) + { + _period = period; + _name = name + "-" + + _counterIds.incrementAndGet(); + reset(); + } + + public void registerEvent() + { + registerEvent(1L); + } + + public void registerEvent(long value) + { + registerEvent(value, System.currentTimeMillis()); + } + + public void registerEvent(long value, long timestamp) + { + if (DISABLE_STATISTICS) + { + return; + } + + long thisSample = (timestamp / _period); + synchronized (this) + { + if (thisSample > _last) + { + _last = thisSample; + _rate = _temp; + _temp = 0L; + if (_rate > _peak) + { + _peak = _rate; + } + } + + _total += value; + _temp += value; + } + } + + /** + * Update the current rate and peak - may reset rate to zero if a new + * sample period has started. + */ + private void update() + { + registerEvent(0L, System.currentTimeMillis()); + } + + /** + * Reset + */ + public void reset() + { + _log.info("Resetting statistics for counter: " + _name); + _peak = 0L; + _rate = 0L; + _total = 0L; + _start = System.currentTimeMillis(); + _last = _start / _period; + } + + public double getPeak() + { + update(); + return (double) _peak / ((double) _period / 1000.0d); + } + + public double getRate() + { + update(); + return (double) _rate / ((double) _period / 1000.0d); + } + + public long getTotal() + { + return _total; + } + + public long getStart() + { + return _start; + } + + public Date getStartTime() + { + return new Date(_start); + } + + public String getName() + { + return _name; + } + + public long getPeriod() + { + return _period; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java b/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java new file mode 100644 index 0000000000..36fec4025a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.stats; + +/** + * This interface is to be implemented by any broker business object that + * wishes to gather statistics about messages delivered through it. + * + * These statistics are exposed using a separate JMX Mbean interface, which + * calls these methods to retrieve the underlying {@link StatisticsCounter}s + * and return their attributes. This interface gives a standard way for + * parts of the broker to set up and configure statistics generation. + * <p> + * When creating these objects, there should be a parent/child relationship + * between them, such that the lowest level gatherer can record staticics if + * enabled, and pass on the notification to the parent object to allow higher + * level aggregation. When resetting statistics, this works in the opposite + * direction, with higher level gatherers also resetting all of their children. + */ +public interface StatisticsGatherer +{ + /** + * Initialise the statistics gathering for this object. + * + * This method is responsible for creating any {@link StatisticsCounter} + * objects and for determining whether statistics generation should be + * enabled, by checking broker and system configuration. + * + * @see StatisticsCounter#DISABLE_STATISTICS + */ + void initialiseStatistics(); + + /** + * This method is responsible for registering the receipt of a message + * with the counters, and also for passing this notification to any parent + * {@link StatisticsGatherer}s. If statistics generation is not enabled, + * then this method should simple delegate to the parent gatherer. + * + * @param messageSize the size in bytes of the delivered message + * @param timestamp the time the message was delivered + */ + void registerMessageReceived(long messageSize, long timestamp); + + /** + * This method is responsible for registering the delivery of a message + * with the counters. Message delivery is recorded by the counter using + * the current system time, as opposed to the message timestamp. + * + * @param messageSize the size in bytes of the delivered message + * @see #registerMessageReceived(long, long) + */ + void registerMessageDelivered(long messageSize); + + /** + * Gives access to the {@link StatisticsCounter} that is used to count + * delivered message statistics. + * + * @return the {@link StatisticsCounter} that counts delivered messages + */ + StatisticsCounter getMessageDeliveryStatistics(); + + /** + * Gives access to the {@link StatisticsCounter} that is used to count + * received message statistics. + * + * @return the {@link StatisticsCounter} that counts received messages + */ + StatisticsCounter getMessageReceiptStatistics(); + + /** + * Gives access to the {@link StatisticsCounter} that is used to count + * delivered message size statistics. + * + * @return the {@link StatisticsCounter} that counts delivered bytes + */ + StatisticsCounter getDataDeliveryStatistics(); + + /** + * Gives access to the {@link StatisticsCounter} that is used to count + * received message size statistics. + * + * @return the {@link StatisticsCounter} that counts received bytes + */ + StatisticsCounter getDataReceiptStatistics(); + + /** + * Reset the counters for this, and any child {@link StatisticsGatherer}s. + */ + void resetStatistics(); + + /** + * Check if this object has statistics generation enabled. + * + * @return true if statistics generation is enabled + */ + boolean isStatisticsEnabled(); + + /** + * Enable or disable statistics generation for this object. + */ + void setStatisticsEnabled(boolean enabled); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java index 2e694b24ea..8b099b62ce 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.store; import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.lang.ref.SoftReference; @@ -479,9 +480,15 @@ public class DerbyMessageStore implements MessageStore FieldTable arguments; if(dataAsBytes.length > 0) { - org.apache.mina.common.ByteBuffer buffer = org.apache.mina.common.ByteBuffer.wrap(dataAsBytes); - arguments = new FieldTable(buffer,buffer.limit()); + try + { + arguments = new FieldTable(new DataInputStream(new ByteArrayInputStream(dataAsBytes)),dataAsBytes.length); + } + catch (IOException e) + { + throw new RuntimeException("IO Exception should not be thrown",e); + } } else { diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java index ce0362d73f..0fd7fdffe5 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java @@ -20,13 +20,21 @@ */ package org.apache.qpid.server.subscription; +import java.util.Map; + import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.flow.FlowCreditManager_0_10; import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.transport.ServerSession; import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.MessageFlowMode; /** * Allows the customisation of the creation of a subscription. This is typically done within an AMQQueue. This factory @@ -56,4 +64,23 @@ public interface SubscriptionFactory RecordDeliveryMethod recordMethod ) throws AMQException; + + + SubscriptionImpl.GetNoAckSubscription createBasicGetNoAckSubscription(AMQChannel channel, + AMQProtocolSession session, + AMQShortString consumerTag, + FieldTable filters, + boolean noLocal, + FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) throws AMQException; + + Subscription_0_10 createSubscription(final ServerSession session, + final String destination, + final MessageAcceptMode acceptMode, + final MessageAcquireMode acquireMode, + final MessageFlowMode flowMode, + final FlowCreditManager_0_10 creditManager, + final FilterManager filterManager, + final Map<String,Object> arguments); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java index 1bba2529c6..1622d63648 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java @@ -20,17 +20,28 @@ */ package org.apache.qpid.server.subscription; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + import org.apache.qpid.AMQException; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; +import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.flow.FlowCreditManager_0_10; import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.transport.ServerSession; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.MessageFlowMode; public class SubscriptionFactoryImpl implements SubscriptionFactory { + private static final AtomicLong SUB_ID_GENERATOR = new AtomicLong(0); + public Subscription createSubscription(int channelId, AMQProtocolSession protocolSession, AMQShortString consumerTag, boolean acks, FieldTable filters, boolean noLocal, FlowCreditManager creditManager) throws AMQException @@ -78,18 +89,47 @@ public class SubscriptionFactoryImpl implements SubscriptionFactory if(isBrowser) { - return new SubscriptionImpl.BrowserSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); + return new SubscriptionImpl.BrowserSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod, getNextSubscriptionId()); } else if(acks) { - return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); + return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod, getNextSubscriptionId()); } else { - return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); + return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod, getNextSubscriptionId()); } } + public SubscriptionImpl.GetNoAckSubscription createBasicGetNoAckSubscription(final AMQChannel channel, + final AMQProtocolSession session, + final AMQShortString consumerTag, + final FieldTable filters, + final boolean noLocal, + final FlowCreditManager creditManager, + final ClientDeliveryMethod deliveryMethod, + final RecordDeliveryMethod recordMethod) throws AMQException + { + return new SubscriptionImpl.GetNoAckSubscription(channel, session, null, null, false, creditManager, deliveryMethod, recordMethod, getNextSubscriptionId()); + } + + public Subscription_0_10 createSubscription(final ServerSession session, + final String destination, + final MessageAcceptMode acceptMode, + final MessageAcquireMode acquireMode, + final MessageFlowMode flowMode, + final FlowCreditManager_0_10 creditManager, + final FilterManager filterManager, + final Map<String,Object> arguments) + { + return new Subscription_0_10(session, destination, acceptMode, acquireMode, + flowMode, creditManager, filterManager, arguments, getNextSubscriptionId()); + } public static final SubscriptionFactoryImpl INSTANCE = new SubscriptionFactoryImpl(); + + private static long getNextSubscriptionId() + { + return SUB_ID_GENERATOR.getAndIncrement(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index d8f44c9f7f..d6a256e2e1 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -88,9 +88,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage private final Lock _stateChangeLock; - private static final AtomicLong idGenerator = new AtomicLong(0); - // Create a simple ID that increments for ever new Subscription - private final long _subscriptionID = idGenerator.getAndIncrement(); + private final long _subscriptionID; private LogSubject _logSubject; private LogActor _logActor; private UUID _id; @@ -104,10 +102,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage AMQShortString consumerTag, FieldTable filters, boolean noLocal, FlowCreditManager creditManager, ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) + RecordDeliveryMethod recordMethod, + long subscriptionID) throws AMQException { - super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod, subscriptionID); } @@ -151,10 +150,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage AMQShortString consumerTag, FieldTable filters, boolean noLocal, FlowCreditManager creditManager, ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) + RecordDeliveryMethod recordMethod, + long subscriptionID) throws AMQException { - super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod, subscriptionID); } @@ -211,16 +211,45 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage } + /** + * NoAck Subscription for use with BasicGet method. + */ + public static final class GetNoAckSubscription extends SubscriptionImpl.NoAckSubscription + { + public GetNoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, + AMQShortString consumerTag, FieldTable filters, + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod, + long subscriptionID) + throws AMQException + { + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod, subscriptionID); + } + + public boolean isTransient() + { + return true; + } + + public boolean wouldSuspend(QueueEntry msg) + { + return !getCreditManager().useCreditForMessage(msg.getMessage()); + } + + } + static final class AckSubscription extends SubscriptionImpl { public AckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, AMQShortString consumerTag, FieldTable filters, boolean noLocal, FlowCreditManager creditManager, ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) + RecordDeliveryMethod recordMethod, + long subscriptionID) throws AMQException { - super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); + super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod, subscriptionID); } @@ -296,10 +325,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage AMQShortString consumerTag, FieldTable arguments, boolean noLocal, FlowCreditManager creditManager, ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) + RecordDeliveryMethod recordMethod, + long subscriptionID) throws AMQException { - + _subscriptionID = subscriptionID; _channel = channel; _consumerTag = consumerTag; @@ -445,7 +475,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage //check that the message hasn't been rejected - if (entry.isRejectedBy(this)) + if (entry.isRejectedBy(getSubscriptionID())) { if (_logger.isDebugEnabled()) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java index 9ea81660c6..3e6299cb8a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java @@ -20,121 +20,108 @@ */ package org.apache.qpid.server.subscription; -import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.subscription.Subscription; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.nio.ByteBuffer; public class SubscriptionList { - private final SubscriptionNode _head = new SubscriptionNode(); - private AtomicReference<SubscriptionNode> _tail = new AtomicReference<SubscriptionNode>(_head); - private AtomicInteger _size = new AtomicInteger(); - + private final AtomicReference<SubscriptionNode> _tail = new AtomicReference<SubscriptionNode>(_head); + private final AtomicReference<SubscriptionNode> _subNodeMarker = new AtomicReference<SubscriptionNode>(_head); + private final AtomicInteger _size = new AtomicInteger(); - public final class SubscriptionNode + public static final class SubscriptionNode { private final AtomicBoolean _deleted = new AtomicBoolean(); private final AtomicReference<SubscriptionNode> _next = new AtomicReference<SubscriptionNode>(); private final Subscription _sub; - public SubscriptionNode() { - + //used for sentinel head and dummy node construction _sub = null; _deleted.set(true); } public SubscriptionNode(final Subscription sub) { + //used for regular node construction _sub = sub; } - - public SubscriptionNode getNext() + /** + * Retrieves the first non-deleted node following the current node. + * Any deleted non-tail nodes encountered during the search are unlinked. + * + * @return the next non-deleted node, or null if none was found. + */ + public SubscriptionNode findNext() { - SubscriptionNode next = nextNode(); while(next != null && next.isDeleted()) { - final SubscriptionNode newNext = next.nextNode(); if(newNext != null) { + //try to move our _next reference forward to the 'newNext' + //node to unlink the deleted node _next.compareAndSet(next, newNext); next = nextNode(); } else { + //'newNext' is null, meaning 'next' is the current tail. Can't unlink + //the tail node for thread safety reasons, just use the null. next = null; } - } + return next; } - private SubscriptionNode nextNode() + /** + * Gets the immediately next referenced node in the structure. + * + * @return the immediately next node in the structure, or null if at the tail. + */ + protected SubscriptionNode nextNode() { return _next.get(); } + /** + * Used to initialise the 'next' reference. Will only succeed if the reference was not previously set. + * + * @param node the SubscriptionNode to set as 'next' + * @return whether the operation succeeded + */ + private boolean setNext(final SubscriptionNode node) + { + return _next.compareAndSet(null, node); + } + public boolean isDeleted() { return _deleted.get(); } - public boolean delete() { - if(_deleted.compareAndSet(false,true)) - { - _size.decrementAndGet(); - advanceHead(); - return true; - } - else - { - return false; - } + return _deleted.compareAndSet(false,true); } - public Subscription getSubscription() { return _sub; } } - - public SubscriptionList(AMQQueue queue) - { - } - - private void advanceHead() - { - SubscriptionNode head = _head.nextNode(); - while(head._next.get() != null && head.isDeleted()) - { - - final SubscriptionNode newhead = head.nextNode(); - if(newhead != null) - { - _head._next.compareAndSet(head, newhead); - } - head = _head.nextNode(); - } - } - - - public SubscriptionNode add(Subscription sub) + private void insert(final SubscriptionNode node, final boolean count) { - SubscriptionNode node = new SubscriptionNode(sub); for (;;) { SubscriptionNode tail = _tail.get(); @@ -143,11 +130,14 @@ public class SubscriptionList { if (next == null) { - if (tail._next.compareAndSet(null, node)) + if (tail.setNext(node)) { _tail.compareAndSet(tail, node); - _size.incrementAndGet(); - return node; + if(count) + { + _size.incrementAndGet(); + } + return; } } else @@ -156,27 +146,101 @@ public class SubscriptionList } } } + } + public void add(final Subscription sub) + { + SubscriptionNode node = new SubscriptionNode(sub); + insert(node, true); } - public boolean remove(Subscription sub) + public boolean remove(final Subscription sub) { - SubscriptionNode node = _head.getNext(); + SubscriptionNode prevNode = _head; + SubscriptionNode node = _head.nextNode(); + while(node != null) { - if(sub.equals(node._sub) && node.delete()) + if(sub.equals(node.getSubscription()) && node.delete()) { + _size.decrementAndGet(); + + SubscriptionNode tail = _tail.get(); + if(node == tail) + { + //we cant remove the last node from the structure for + //correctness reasons, however we have just 'deleted' + //the tail. Inserting an empty dummy node after it will + //let us scavenge the node containing the Subscription. + insert(new SubscriptionNode(), false); + } + + //advance the next node reference in the 'prevNode' to scavange + //the newly 'deleted' node for the Subscription. + prevNode.findNext(); + + nodeMarkerCleanup(node); + return true; } - node = node.getNext(); + + prevNode = node; + node = node.findNext(); } + return false; } + private void nodeMarkerCleanup(final SubscriptionNode node) + { + SubscriptionNode markedNode = _subNodeMarker.get(); + if(node == markedNode) + { + //if the marked node is the one we are removing, then + //replace it with a dummy pointing at the next node. + //this is OK as the marked node is only used to index + //into the list and find the next node to use. + //Because we inserted a dummy if node was the + //tail, markedNode.nextNode() can never be null. + SubscriptionNode dummy = new SubscriptionNode(); + dummy.setNext(markedNode.nextNode()); + + //if the CAS fails the marked node has changed, thus + //we don't care about the dummy and just forget it + _subNodeMarker.compareAndSet(markedNode, dummy); + } + else if(markedNode != null) + { + //if the marked node was already deleted then it could + //hold subsequently removed nodes after it in the list + //in memory. Scavenge it to ensure their actual removal. + if(markedNode != _head && markedNode.isDeleted()) + { + markedNode.findNext(); + } + } + } - public static class SubscriptionNodeIterator + public boolean updateMarkedNode(final SubscriptionNode expected, final SubscriptionNode nextNode) + { + return _subNodeMarker.compareAndSet(expected, nextNode); + } + + /** + * Get the current marked SubscriptionNode. This should only be used only to index into the list and find the next node + * after the mark, since if the previously marked node was subsequently deleted the item returned may be a dummy node + * with reference to the next node. + * + * @return the previously marked node (or a dummy if it was subsequently deleted) + */ + public SubscriptionNode getMarkedNode() { + return _subNodeMarker.get(); + } + + public static class SubscriptionNodeIterator + { private SubscriptionNode _lastNode; SubscriptionNodeIterator(SubscriptionNode startNode) @@ -184,49 +248,25 @@ public class SubscriptionList _lastNode = startNode; } - - public boolean atTail() - { - return _lastNode.nextNode() == null; - } - public SubscriptionNode getNode() { - return _lastNode; - } public boolean advance() { + SubscriptionNode nextNode = _lastNode.findNext(); + _lastNode = nextNode; - if(!atTail()) - { - SubscriptionNode nextNode = _lastNode.nextNode(); - while(nextNode.isDeleted() && nextNode.nextNode() != null) - { - nextNode = nextNode.nextNode(); - } - _lastNode = nextNode; - return true; - - } - else - { - return false; - } - + return _lastNode != null; } - } - public SubscriptionNodeIterator iterator() { return new SubscriptionNodeIterator(_head); } - public SubscriptionNode getHead() { return _head; @@ -236,9 +276,6 @@ public class SubscriptionList { return _size.get(); } - - - } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java index b36ac84cdd..c5d6bc203c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java @@ -40,7 +40,6 @@ import org.apache.qpid.server.logging.actors.GenericActor; import org.apache.qpid.server.logging.messages.SubscriptionMessages; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.SubscriptionActor; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.AMQMessage; @@ -80,10 +79,7 @@ import java.nio.ByteBuffer; public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCreditManagerListener, SubscriptionConfig, LogSubject { - - private static final AtomicLong idGenerator = new AtomicLong(0); - // Create a simple ID that increments for ever new Subscription - private final long _subscriptionID = idGenerator.getAndIncrement(); + private final long _subscriptionID; private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); private final QueueEntry.SubscriptionAssignedState _assignedState = new QueueEntry.SubscriptionAssignedState(this); @@ -97,7 +93,6 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr private FlowCreditManager_0_10 _creditManager; - private StateListener _stateListener = new StateListener() { @@ -114,16 +109,15 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr private final MessageAcquireMode _acquireMode; private MessageFlowMode _flowMode; private final ServerSession _session; - private AtomicBoolean _stopped = new AtomicBoolean(true); - private ConcurrentHashMap<Integer, QueueEntry> _sentMap = new ConcurrentHashMap<Integer, QueueEntry>(); + private final AtomicBoolean _stopped = new AtomicBoolean(true); private static final Struct[] EMPTY_STRUCT_ARRAY = new Struct[0]; private LogActor _logActor; - private Map<String, Object> _properties = new ConcurrentHashMap<String, Object>(); + private final Map<String, Object> _properties = new ConcurrentHashMap<String, Object>(); private UUID _id; private String _traceExclude; private String _trace; - private long _createTime = System.currentTimeMillis(); + private final long _createTime = System.currentTimeMillis(); private final AtomicLong _deliveredCount = new AtomicLong(0); private final Map<String, Object> _arguments; @@ -132,8 +126,9 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr MessageAcquireMode acquireMode, MessageFlowMode flowMode, FlowCreditManager_0_10 creditManager, - FilterManager filters,Map<String, Object> arguments) + FilterManager filters,Map<String, Object> arguments, long subscriptionId) { + _subscriptionID = subscriptionId; _session = session; _destination = destination; _acceptMode = acceptMode; @@ -199,7 +194,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public boolean isSuspended() { - return !isActive() || _deleted.get(); // TODO check for Session suspension + return !isActive() || _deleted.get() || _session.isClosing(); // TODO check for Session suspension } public boolean hasInterest(QueueEntry entry) @@ -208,7 +203,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr //check that the message hasn't been rejected - if (entry.isRejectedBy(this)) + if (entry.isRejectedBy(getSubscriptionID())) { return false; @@ -442,7 +437,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr Struct[] headers = new Struct[] { deliveryProps, messageProps }; BasicContentHeaderProperties properties = - (BasicContentHeaderProperties) message_0_8.getContentHeaderBody().properties; + (BasicContentHeaderProperties) message_0_8.getContentHeaderBody().getProperties(); final AMQShortString exchange = message_0_8.getMessagePublishInfo().getExchange(); if(exchange != null) { @@ -732,13 +727,22 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public void stop() { - if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) + try { - _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); + getSendLock(); + + if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) + { + _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); + } + _stopped.set(true); + FlowCreditManager_0_10 creditManager = getCreditManager(); + creditManager.clearCredit(); + } + finally + { + releaseSendLock(); } - _stopped.set(true); - FlowCreditManager_0_10 creditManager = getCreditManager(); - creditManager.clearCredit(); } public void addCredit(MessageCreditUnit unit, long value) diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java b/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java index 3ca22b60c8..abbc5a3805 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/QpidAcceptor.java +++ b/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.qpid.transport.NetworkDriver; +import org.apache.qpid.transport.network.NetworkTransport; public class QpidAcceptor { - NetworkDriver _driver; + NetworkTransport _transport; String _protocol; - public QpidAcceptor(NetworkDriver driver, String protocol) + public QpidAcceptor(NetworkTransport transport, String protocol) { - _driver = driver; + _transport = transport; _protocol = protocol; } - public NetworkDriver getNetworkDriver() + public NetworkTransport getNetworkTransport() { - return _driver; + return _transport; } public String toString() diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java index d2addfde0c..d83013afba 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java @@ -20,11 +20,19 @@ */ package org.apache.qpid.server.transport; -import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.*; +import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.CONNECTION_FORMAT; +import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SOCKET_FORMAT; +import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.USER_FORMAT; +import java.security.Principal; import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import javax.security.auth.Subject; + import org.apache.qpid.AMQException; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.configuration.ConnectionConfig; @@ -35,23 +43,39 @@ import org.apache.qpid.server.logging.actors.GenericActor; import org.apache.qpid.server.logging.messages.ConnectionMessages; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.security.AuthorizationHolder; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.ConnectionCloseCode; import org.apache.qpid.transport.ExecutionErrorCode; import org.apache.qpid.transport.ExecutionException; import org.apache.qpid.transport.Method; import org.apache.qpid.transport.ProtocolEvent; +import org.apache.qpid.transport.Session; -public class ServerConnection extends Connection implements AMQConnectionModel, LogSubject +public class ServerConnection extends Connection implements AMQConnectionModel, LogSubject, AuthorizationHolder { private ConnectionConfig _config; private Runnable _onOpenTask; private AtomicBoolean _logClosed = new AtomicBoolean(false); private LogActor _actor = GenericActor.getInstance(this); - public ServerConnection() + private Subject _authorizedSubject = null; + private Principal _authorizedPrincipal = null; + private boolean _statisticsEnabled = false; + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; + private final long _connectionId; + + public ServerConnection(final long connectionId) { + _connectionId = connectionId; + } + public UUID getId() + { + return _config.getId(); } @Override @@ -72,8 +96,18 @@ public class ServerConnection extends Connection implements AMQConnectionModel, _onOpenTask.run(); } _actor.message(ConnectionMessages.OPEN(getClientId(), "0-10", true, true)); + + getVirtualHost().getConnectionRegistry().registerConnection(this); } - + + if (state == State.CLOSE_RCVD || state == State.CLOSED || state == State.CLOSING) + { + if(_virtualHost != null) + { + _virtualHost.getConnectionRegistry().deregisterConnection(this); + } + } + if (state == State.CLOSED) { logClosed(); @@ -110,6 +144,8 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public void setVirtualHost(VirtualHost virtualHost) { _virtualHost = virtualHost; + + initialiseStatistics(); } public void setConnectionConfig(final ConnectionConfig config) @@ -145,6 +181,11 @@ public class ServerConnection extends Connection implements AMQConnectionModel, ((ServerSession)session).close(); } + + public LogSubject getLogSubject() + { + return (LogSubject) this; + } @Override public void received(ProtocolEvent event) @@ -179,9 +220,9 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public String toLogString() { boolean hasVirtualHost = (null != this.getVirtualHost()); - boolean hasPrincipal = (null != getAuthorizationID()); + boolean hasClientId = (null != getClientId()); - if (hasPrincipal && hasVirtualHost) + if (hasClientId && hasVirtualHost) { return "[" + MessageFormat.format(CONNECTION_FORMAT, @@ -191,7 +232,7 @@ public class ServerConnection extends Connection implements AMQConnectionModel, getVirtualHost().getName()) + "] "; } - else if (hasPrincipal) + else if (hasClientId) { return "[" + MessageFormat.format(USER_FORMAT, @@ -215,4 +256,147 @@ public class ServerConnection extends Connection implements AMQConnectionModel, { return _actor; } + + public void close(AMQConstant cause, String message) throws AMQException + { + ConnectionCloseCode replyCode = ConnectionCloseCode.NORMAL; + try + { + replyCode = ConnectionCloseCode.get(cause.getCode()); + } + catch (IllegalArgumentException iae) + { + // Ignore + } + close(replyCode, message); + } + + public List<AMQSessionModel> getSessionModels() + { + List<AMQSessionModel> sessions = new ArrayList<AMQSessionModel>(); + for (Session ssn : getChannels()) + { + sessions.add((AMQSessionModel) ssn); + } + return sessions; + } + + public void registerMessageDelivered(long messageSize) + { + if (isStatisticsEnabled()) + { + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); + } + _virtualHost.registerMessageDelivered(messageSize); + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + if (isStatisticsEnabled()) + { + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); + } + _virtualHost.registerMessageReceived(messageSize, timestamp); + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return _messagesReceived; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return _dataReceived; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return _messagesDelivered; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return _dataDelivered; + } + + public void resetStatistics() + { + _messagesDelivered.reset(); + _dataDelivered.reset(); + _messagesReceived.reset(); + _dataReceived.reset(); + } + + public void initialiseStatistics() + { + setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && + _virtualHost.getApplicationRegistry().getConfiguration().isStatisticsGenerationConnectionsEnabled()); + + _messagesDelivered = new StatisticsCounter("messages-delivered-" + getConnectionId()); + _dataDelivered = new StatisticsCounter("data-delivered-" + getConnectionId()); + _messagesReceived = new StatisticsCounter("messages-received-" + getConnectionId()); + _dataReceived = new StatisticsCounter("data-received-" + getConnectionId()); + } + + public boolean isStatisticsEnabled() + { + return _statisticsEnabled; + } + + public void setStatisticsEnabled(boolean enabled) + { + _statisticsEnabled = enabled; + } + + /** + * @return authorizedSubject + */ + public Subject getAuthorizedSubject() + { + return _authorizedSubject; + } + + /** + * Sets the authorized subject. It also extracts the UsernamePrincipal from the subject + * and caches it for optimisation purposes. + * + * @param authorizedSubject + */ + public void setAuthorizedSubject(final Subject authorizedSubject) + { + if (authorizedSubject == null) + { + _authorizedSubject = null; + _authorizedPrincipal = null; + } + else + { + _authorizedSubject = authorizedSubject; + _authorizedPrincipal = UsernamePrincipal.getUsernamePrincipalFromSubject(_authorizedSubject); + } + } + + public Principal getAuthorizedPrincipal() + { + return _authorizedPrincipal; + } + + public long getConnectionId() + { + return _connectionId; + } + + @Override + public boolean isSessionNameUnique(String name) + { + return !super.hasSessionWithName(name); + } + + @Override + public String getUserName() + { + return _authorizedPrincipal.getName(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index 2b9e92f685..2de8a0425e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -20,26 +20,47 @@ */ package org.apache.qpid.server.transport; -import org.apache.qpid.transport.*; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.GenericActor; -import org.apache.qpid.common.ClientProperties; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + import org.apache.qpid.protocol.ProtocolEngine; -import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.subscription.Subscription_0_10; import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.security.sasl.SaslServer; -import javax.security.sasl.SaslException; -import java.util.*; +import org.apache.qpid.transport.Binary; +import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.ConnectionClose; +import org.apache.qpid.transport.ConnectionCloseCode; +import org.apache.qpid.transport.ConnectionOpen; +import org.apache.qpid.transport.ConnectionOpenOk; +import org.apache.qpid.transport.ConnectionTuneOk; +import org.apache.qpid.transport.ServerDelegate; +import org.apache.qpid.transport.Session; +import org.apache.qpid.transport.SessionAttach; +import org.apache.qpid.transport.SessionDelegate; +import org.apache.qpid.transport.SessionDetach; +import org.apache.qpid.transport.SessionDetachCode; +import org.apache.qpid.transport.SessionDetached; public class ServerConnectionDelegate extends ServerDelegate { private String _localFQDN; private final IApplicationRegistry _appRegistry; - public ServerConnectionDelegate(IApplicationRegistry appRegistry, String localFQDN) { this(new HashMap<String,Object>(Collections.singletonMap("qpid.federation_tag",appRegistry.getBroker().getFederationTag())), Collections.singletonList((Object)"en_US"), appRegistry, localFQDN); @@ -68,24 +89,42 @@ public class ServerConnectionDelegate extends ServerDelegate return list; } - @Override public ServerSession getSession(Connection conn, SessionAttach atc) { - SessionDelegate serverSessionDelegate = new ServerSessionDelegate(_appRegistry); + SessionDelegate serverSessionDelegate = new ServerSessionDelegate(); ServerSession ssn = new ServerSession(conn, serverSessionDelegate, new Binary(atc.getName()), 0); return ssn; } - @Override protected SaslServer createSaslServer(String mechanism) throws SaslException { return _appRegistry.getAuthenticationManager().createSaslServer(mechanism, _localFQDN); } - @Override + protected void secure(final SaslServer ss, final Connection conn, final byte[] response) + { + final AuthenticationResult authResult = _appRegistry.getAuthenticationManager().authenticate(ss, response); + final ServerConnection sconn = (ServerConnection) conn; + + + if (AuthenticationStatus.SUCCESS.equals(authResult.getStatus())) + { + tuneAuthorizedConnection(sconn); + sconn.setAuthorizedSubject(authResult.getSubject()); + } + else if (AuthenticationStatus.CONTINUE.equals(authResult.getStatus())) + { + connectionAuthContinue(sconn, authResult.getChallenge()); + } + else + { + connectionAuthFailed(sconn, authResult.getCause()); + } + } + public void connectionClose(Connection conn, ConnectionClose close) { try @@ -99,10 +138,9 @@ public class ServerConnectionDelegate extends ServerDelegate } - @Override public void connectionOpen(Connection conn, ConnectionOpen open) { - ServerConnection sconn = (ServerConnection) conn; + final ServerConnection sconn = (ServerConnection) conn; VirtualHost vhost; String vhostName; @@ -116,7 +154,7 @@ public class ServerConnectionDelegate extends ServerDelegate } vhost = _appRegistry.getVirtualHostRegistry().getVirtualHost(vhostName); - SecurityManager.setThreadPrincipal(conn.getAuthorizationID()); + SecurityManager.setThreadSubject(sconn.getAuthorizedSubject()); if(vhost != null) { @@ -138,6 +176,27 @@ public class ServerConnectionDelegate extends ServerDelegate sconn.invoke(new ConnectionClose(ConnectionCloseCode.INVALID_PATH, "Unknown virtualhost '"+vhostName+"'")); sconn.setState(Connection.State.CLOSING); } + + } + + @Override + public void connectionTuneOk(final Connection conn, final ConnectionTuneOk ok) + { + ServerConnection sconn = (ServerConnection) conn; + int okChannelMax = ok.getChannelMax(); + + if (okChannelMax > getChannelMax()) + { + _logger.error("Connection '" + sconn.getConnectionId() + "' being severed, " + + "client connectionTuneOk returned a channelMax (" + okChannelMax + + ") above the servers offered limit (" + getChannelMax() +")"); + + //Due to the error we must forcefully close the connection without negotiation + sconn.getSender().close(); + return; + } + + setConnectionTuneOkChannelMax(sconn, okChannelMax); } @Override @@ -152,4 +211,59 @@ public class ServerConnectionDelegate extends ServerDelegate { return ApplicationRegistry.getInstance().getConfiguration().getMaxChannelCount(); } + + @Override public void sessionDetach(Connection conn, SessionDetach dtc) + { + // To ensure a clean detach, we unregister any remaining subscriptions. Unregister ensures + // that any in-progress delivery (SubFlushRunner/QueueRunner) is completed before the unregister + // completes. + unregisterAllSubscriptions(conn, dtc); + super.sessionDetach(conn, dtc); + } + + private void unregisterAllSubscriptions(Connection conn, SessionDetach dtc) + { + final ServerSession ssn = (ServerSession) conn.getSession(dtc.getChannel()); + final Collection<Subscription_0_10> subs = ssn.getSubscriptions(); + for (Subscription_0_10 subscription_0_10 : subs) + { + ssn.unregister(subscription_0_10); + } + } + + @Override + public void sessionAttach(final Connection conn, final SessionAttach atc) + { + final String clientId = new String(atc.getName()); + final Session ssn = getSession(conn, atc); + + if(isSessionNameUnique(clientId,conn)) + { + conn.registerSession(ssn); + super.sessionAttach(conn, atc); + } + else + { + ssn.invoke(new SessionDetached(atc.getName(), SessionDetachCode.SESSION_BUSY)); + ssn.closed(); + } + } + + private boolean isSessionNameUnique(final String name, final Connection conn) + { + final ServerConnection sconn = (ServerConnection) conn; + final String userId = sconn.getUserName(); + + final Iterator<AMQConnectionModel> connections = + ((ServerConnection)conn).getVirtualHost().getConnectionRegistry().getConnections().iterator(); + while(connections.hasNext()) + { + final AMQConnectionModel amqConnectionModel = (AMQConnectionModel) connections.next(); + if (userId.equals(amqConnectionModel.getUserName()) && !amqConnectionModel.isSessionNameUnique(name)) + { + return false; + } + } + return true; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java index 540ad3fffd..67ddd6ca77 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java @@ -23,9 +23,25 @@ package org.apache.qpid.server.transport; import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.CHANNEL_FORMAT; import static org.apache.qpid.util.Serial.gt; -import com.sun.security.auth.UserPrincipal; +import java.lang.ref.WeakReference; +import java.security.Principal; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; + +import javax.security.auth.Subject; import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.server.configuration.ConfigStore; import org.apache.qpid.server.configuration.ConfiguredObject; @@ -38,18 +54,18 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.GenericActor; import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.security.PrincipalHolder; +import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.subscription.Subscription_0_10; import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.protocol.AMQSessionModel; -import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.transport.Binary; import org.apache.qpid.transport.Connection; import org.apache.qpid.transport.MessageTransfer; @@ -58,24 +74,13 @@ import org.apache.qpid.transport.Range; import org.apache.qpid.transport.RangeSet; import org.apache.qpid.transport.Session; import org.apache.qpid.transport.SessionDelegate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.lang.ref.WeakReference; -import java.security.Principal; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicLong; - -public class ServerSession extends Session implements PrincipalHolder, SessionConfig, AMQSessionModel, LogSubject +public class ServerSession extends Session implements AuthorizationHolder, SessionConfig, AMQSessionModel, LogSubject { + private static final Logger _logger = LoggerFactory.getLogger(ServerSession.class); + private static final String NULL_DESTINTATION = UUID.randomUUID().toString(); private final UUID _id; @@ -111,8 +116,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo private final AtomicLong _txnCommits = new AtomicLong(0); private final AtomicLong _txnRejects = new AtomicLong(0); private final AtomicLong _txnCount = new AtomicLong(0); - - private Principal _principal; + private final AtomicLong _txnUpdateTime = new AtomicLong(0); private Map<String, Subscription_0_10> _subscriptions = new ConcurrentHashMap<String, Subscription_0_10>(); @@ -125,27 +129,27 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo this(connection, delegate, name, expiry, ((ServerConnection)connection).getConfig()); } - protected void setState(State state) - { - super.setState(state); - - if (state == State.OPEN) - { - _actor.message(ChannelMessages.CREATE()); - } - } - public ServerSession(Connection connection, SessionDelegate delegate, Binary name, long expiry, ConnectionConfig connConfig) { super(connection, delegate, name, expiry); _connectionConfig = connConfig; _transaction = new AutoCommitTransaction(this.getMessageStore()); - _principal = new UserPrincipal(connection.getAuthorizationID()); - _reference = new WeakReference(this); + + _reference = new WeakReference<Session>(this); _id = getConfigStore().createId(); getConfigStore().addConfiguredObject(this); } + protected void setState(State state) + { + super.setState(state); + + if (state == State.OPEN) + { + _actor.message(ChannelMessages.CREATE()); + } + } + private ConfigStore getConfigStore() { return getConnectionConfig().getConfigStore(); @@ -160,8 +164,8 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo public void enqueue(final ServerMessage message, final ArrayList<? extends BaseQueue> queues) { - - _transaction.enqueue(queues,message, new ServerTransaction.Action() + getConnectionModel().registerMessageReceived(message.getSize(), message.getArrivalTime()); + _transaction.enqueue(queues,message, new ServerTransaction.Action() { BaseQueue[] _queues = queues.toArray(new BaseQueue[queues.size()]); @@ -189,12 +193,14 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo }); incrementOutstandingTxnsIfNecessary(); + updateTransactionalActivity(); } public void sendMessage(MessageTransfer xfr, Runnable postIdSettingAction) { + getConnectionModel().registerMessageDelivered(xfr.getBodySize()); invoke(xfr, postIdSettingAction); } @@ -377,6 +383,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo entry.release(); } }); + updateTransactionalActivity(); } public Collection<Subscription_0_10> getSubscriptions() @@ -410,7 +417,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo catch (AMQException e) { // TODO - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + _logger.error("Failed to unregister subscription", e); } finally { @@ -425,6 +432,11 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo // theory return !(_transaction instanceof AutoCommitTransaction); } + + public boolean inTransaction() + { + return isTransactional() && _txnUpdateTime.get() > 0 && _transaction.getTransactionStartTime() > 0; + } public void selectTx() { @@ -471,6 +483,17 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo } } + /** + * Update last transaction activity timestamp + */ + public void updateTransactionalActivity() + { + if (isTransactional()) + { + _txnUpdateTime.set(System.currentTimeMillis()); + } + } + public Long getTxnStarts() { return _txnStarts.get(); @@ -491,9 +514,14 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo return _txnCount.get(); } - public Principal getPrincipal() + public Principal getAuthorizedPrincipal() { - return _principal; + return ((ServerConnection) getConnection()).getAuthorizedPrincipal(); + } + + public Subject getAuthorizedSubject() + { + return ((ServerConnection) getConnection()).getAuthorizedSubject(); } public void addSessionCloseTask(Task task) @@ -606,18 +634,61 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo return (LogSubject) this; } - @Override + public void checkTransactionStatus(long openWarn, long openClose, long idleWarn, long idleClose) throws AMQException + { + if (inTransaction()) + { + long currentTime = System.currentTimeMillis(); + long openTime = currentTime - _transaction.getTransactionStartTime(); + long idleTime = currentTime - _txnUpdateTime.get(); + + // Log a warning on idle or open transactions + if (idleWarn > 0L && idleTime > idleWarn) + { + CurrentActor.get().message(getLogSubject(), ChannelMessages.IDLE_TXN(idleTime)); + _logger.warn("IDLE TRANSACTION ALERT " + getLogSubject().toString() + " " + idleTime + " ms"); + } + else if (openWarn > 0L && openTime > openWarn) + { + CurrentActor.get().message(getLogSubject(), ChannelMessages.OPEN_TXN(openTime)); + _logger.warn("OPEN TRANSACTION ALERT " + getLogSubject().toString() + " " + openTime + " ms"); + } + + // Close connection for idle or open transactions that have timed out + if (idleClose > 0L && idleTime > idleClose) + { + getConnectionModel().closeSession(this, AMQConstant.RESOURCE_ERROR, "Idle transaction timed out"); + } + else if (openClose > 0L && openTime > openClose) + { + getConnectionModel().closeSession(this, AMQConstant.RESOURCE_ERROR, "Open transaction timed out"); + } + } + } + public String toLogString() { return "[" + MessageFormat.format(CHANNEL_FORMAT, - getConnection().getConnectionId(), + ((ServerConnection) getConnection()).getConnectionId(), getClientID(), ((ProtocolEngine) _connectionConfig).getRemoteAddress().toString(), getVirtualHost().getName(), getChannel()) + "] "; - } + @Override + public void close() + { + // unregister subscriptions in order to prevent sending of new messages + // to subscriptions with closing session + final Collection<Subscription_0_10> subscriptions = getSubscriptions(); + for (Subscription_0_10 subscription_0_10 : subscriptions) + { + unregister(subscription_0_10); + } + + super.close(); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java index 42a3975e24..17bd06538f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java @@ -25,31 +25,34 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Map; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQUnknownExchangeType; -import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.exchange.*; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeInUseException; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.exchange.ExchangeType; +import org.apache.qpid.server.exchange.HeadersExchange; import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.filter.FilterManagerFactory; import org.apache.qpid.server.flow.FlowCreditManager_0_10; import org.apache.qpid.server.flow.WindowCreditManager; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.GenericActor; +import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.message.MessageMetaData_0_10; import org.apache.qpid.server.message.MessageTransferMessage; -import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.subscription.Subscription_0_10; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Acquired; @@ -95,26 +98,34 @@ import org.apache.qpid.transport.TxSelect; public class ServerSessionDelegate extends SessionDelegate { - private final IApplicationRegistry _appRegistry; + private static final Logger LOGGER = Logger.getLogger(ServerSessionDelegate.class); - public ServerSessionDelegate(IApplicationRegistry appRegistry) + public ServerSessionDelegate() { - _appRegistry = appRegistry; + } @Override public void command(Session session, Method method) { - SecurityManager.setThreadPrincipal(session.getConnection().getAuthorizationID()); - - if(!session.isClosing()) + try { - super.command(session, method); - if (method.isSync()) + setThreadSubject(session); + + if(!session.isClosing()) { - session.flushProcessed(); + super.command(session, method); + if (method.isSync()) + { + session.flushProcessed(); + } } } + catch(RuntimeException e) + { + LOGGER.error("Exception processing command", e); + exception(session, method, ExecutionErrorCode.INTERNAL_ERROR, "Exception processing command: " + e); + } } @Override @@ -123,8 +134,6 @@ public class ServerSessionDelegate extends SessionDelegate ((ServerSession)session).accept(method.getTransfers()); } - - @Override public void messageReject(Session session, MessageReject method) { @@ -159,7 +168,6 @@ public class ServerSessionDelegate extends SessionDelegate @Override public void messageSubscribe(Session session, MessageSubscribe method) { - //TODO - work around broken Python tests if(!method.hasAcceptMode()) { @@ -203,32 +211,33 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session,method,ExecutionErrorCode.NOT_FOUND, "Queue: " + queueName + " not found"); } - else if(queue.getPrincipalHolder() != null && queue.getPrincipalHolder() != session) + else if(queue.getAuthorizationHolder() != null && queue.getAuthorizationHolder() != session) { exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); } else { - if(queue.isExclusive()) { - if(queue.getPrincipalHolder() == null) + ServerSession s = (ServerSession) session; + queue.setExclusiveOwningSession(s); + if(queue.getAuthorizationHolder() == null) { - queue.setPrincipalHolder((ServerSession)session); + queue.setAuthorizationHolder(s); + queue.setExclusiveOwningSession(s); ((ServerSession) session).addSessionCloseTask(new ServerSession.Task() { - public void doTask(ServerSession session) { - if(queue.getPrincipalHolder() == session) + if(queue.getAuthorizationHolder() == session) { - queue.setPrincipalHolder(null); + queue.setAuthorizationHolder(null); + queue.setExclusiveOwningSession(null); } } }); } - } FlowCreditManager_0_10 creditManager = new WindowCreditManager(0L,0L); @@ -244,7 +253,7 @@ public class ServerSessionDelegate extends SessionDelegate return; } - Subscription_0_10 sub = new Subscription_0_10((ServerSession)session, + Subscription_0_10 sub = SubscriptionFactoryImpl.INSTANCE.createSubscription((ServerSession)session, destination, method.getAcceptMode(), method.getAcquireMode(), @@ -275,25 +284,10 @@ public class ServerSessionDelegate extends SessionDelegate } } - @Override public void messageTransfer(Session ssn, MessageTransfer xfr) { - ExchangeRegistry exchangeRegistry = getExchangeRegistry(ssn); - Exchange exchange; - if(xfr.hasDestination()) - { - exchange = exchangeRegistry.getExchange(xfr.getDestination()); - if(exchange == null) - { - exchange = exchangeRegistry.getDefaultExchange(); - } - } - else - { - exchange = exchangeRegistry.getDefaultExchange(); - } - + final Exchange exchange = getExchangeForMessage(ssn, xfr); DeliveryProperties delvProps = null; if(xfr.getHeader() != null && (delvProps = xfr.getHeader().get(DeliveryProperties.class)) != null && delvProps.hasTtl() && !delvProps.hasExpiration()) @@ -301,7 +295,7 @@ public class ServerSessionDelegate extends SessionDelegate delvProps.setExpiration(System.currentTimeMillis() + delvProps.getTtl()); } - MessageMetaData_0_10 messageMetaData = new MessageMetaData_0_10(xfr); + final MessageMetaData_0_10 messageMetaData = new MessageMetaData_0_10(xfr); if (!getVirtualHost(ssn).getSecurityManager().authorisePublish(messageMetaData.isImmediate(), messageMetaData.getRoutingKey(), exchange.getName())) { @@ -311,65 +305,63 @@ public class ServerSessionDelegate extends SessionDelegate return; } - - final MessageStore store = getVirtualHost(ssn).getMessageStore(); - StoredMessage<MessageMetaData_0_10> storeMessage = store.addMessage(messageMetaData); - ByteBuffer body = xfr.getBody(); - if(body != null) + + final Exchange exchangeInUse; + ArrayList<? extends BaseQueue> queues = exchange.route(messageMetaData); + if(queues.isEmpty() && exchange.getAlternateExchange() != null) { - storeMessage.addContent(0, body); + final Exchange alternateExchange = exchange.getAlternateExchange(); + queues = alternateExchange.route(messageMetaData); + if (!queues.isEmpty()) + { + exchangeInUse = alternateExchange; + } + else + { + exchangeInUse = exchange; + } + } + else + { + exchangeInUse = exchange; } - storeMessage.flushToStore(); - MessageTransferMessage message = new MessageTransferMessage(storeMessage, ((ServerSession)ssn).getReference()); - - ArrayList<? extends BaseQueue> queues = exchange.route(message); - - - if(queues != null && queues.size() != 0) + if(!queues.isEmpty()) { + final MessageStore store = getVirtualHost(ssn).getMessageStore(); + final StoredMessage<MessageMetaData_0_10> storeMessage = createAndFlushStoreMessage(xfr, messageMetaData, store); + MessageTransferMessage message = new MessageTransferMessage(storeMessage, ((ServerSession)ssn).getReference()); ((ServerSession) ssn).enqueue(message, queues); } else { - if(delvProps == null || !delvProps.hasDiscardUnroutable() || !delvProps.getDiscardUnroutable()) + if((delvProps == null || !delvProps.getDiscardUnroutable()) && xfr.getAcceptMode() == MessageAcceptMode.EXPLICIT) { - if(xfr.getAcceptMode() == MessageAcceptMode.EXPLICIT) - { - RangeSet rejects = new RangeSet(); - rejects.add(xfr.getId()); - MessageReject reject = new MessageReject(rejects, MessageRejectCode.UNROUTABLE, "Unroutable"); - ssn.invoke(reject); - } - else - { - Exchange alternate = exchange.getAlternateExchange(); - if(alternate != null) - { - queues = alternate.route(message); - if(queues != null && queues.size() != 0) - { - ((ServerSession) ssn).enqueue(message, queues); - } - else - { - //TODO - log the message discard - } - } - else - { - //TODO - log the message discard - } - - - } + RangeSet rejects = new RangeSet(); + rejects.add(xfr.getId()); + MessageReject reject = new MessageReject(rejects, MessageRejectCode.UNROUTABLE, "Unroutable"); + ssn.invoke(reject); + } + else + { + ((ServerSession) ssn).getLogActor().message(ExchangeMessages.DISCARDMSG(exchangeInUse.getName(), messageMetaData.getRoutingKey())); } - - } ssn.processed(xfr); + } + private StoredMessage<MessageMetaData_0_10> createAndFlushStoreMessage(final MessageTransfer xfr, + final MessageMetaData_0_10 messageMetaData, final MessageStore store) + { + final StoredMessage<MessageMetaData_0_10> storeMessage = store.addMessage(messageMetaData); + ByteBuffer body = xfr.getBody(); + if(body != null) + { + storeMessage.addContent(0, body); + } + storeMessage.flushToStore(); + return storeMessage; } @Override @@ -389,7 +381,7 @@ public class ServerSessionDelegate extends SessionDelegate ((ServerSession)session).unregister(sub); if(!queue.isDeleted() && queue.isExclusive() && queue.getConsumerCount() == 0) { - queue.setPrincipalHolder(null); + queue.setAuthorizationHolder(null); } } } @@ -448,6 +440,19 @@ public class ServerSessionDelegate extends SessionDelegate VirtualHost virtualHost = getVirtualHost(session); Exchange exchange = getExchange(session, exchangeName); + //we must check for any unsupported arguments present and throw not-implemented + if(method.hasArguments()) + { + Map<String,Object> args = method.getArguments(); + + //QPID-3392: currently we don't support any! + if(!args.isEmpty()) + { + exception(session, method, ExecutionErrorCode.NOT_IMPLEMENTED, "Unsupported exchange argument(s) found " + args.keySet().toString()); + return; + } + } + if(method.getPassive()) { if(exchange == null) @@ -457,7 +462,6 @@ public class ServerSessionDelegate extends SessionDelegate } else { - // TODO - check exchange has same properties if(!exchange.getTypeShortString().toString().equals(method.getType())) { exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Cannot redeclare with a different exchange type"); @@ -562,6 +566,25 @@ public class ServerSessionDelegate extends SessionDelegate } + private Exchange getExchangeForMessage(Session ssn, MessageTransfer xfr) + { + final ExchangeRegistry exchangeRegistry = getExchangeRegistry(ssn); + Exchange exchange; + if(xfr.hasDestination()) + { + exchange = exchangeRegistry.getExchange(xfr.getDestination()); + if(exchange == null) + { + exchange = exchangeRegistry.getDefaultExchange(); + } + } + else + { + exchange = exchangeRegistry.getDefaultExchange(); + } + return exchange; + } + private VirtualHost getVirtualHost(Session session) { ServerConnection conn = getServerConnection(session); @@ -583,6 +606,12 @@ public class ServerSessionDelegate extends SessionDelegate try { + if (nameNullOrEmpty(method.getExchange())) + { + exception(session, method, ExecutionErrorCode.INVALID_ARGUMENT, "Delete not allowed for default exchange"); + return; + } + Exchange exchange = getExchange(session, method.getExchange()); if(exchange == null) @@ -618,6 +647,16 @@ public class ServerSessionDelegate extends SessionDelegate } } + private boolean nameNullOrEmpty(String name) + { + if(name == null || name.length() == 0) + { + return true; + } + + return false; + } + private boolean isStandardExchange(Exchange exchange, Collection<ExchangeType<? extends Exchange>> registeredTypes) { for(ExchangeType type : registeredTypes) @@ -664,9 +703,9 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "queue not set"); } - else if (!method.hasExchange()) + else if (nameNullOrEmpty(method.getExchange())) { - exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "exchange not set"); + exception(session, method, ExecutionErrorCode.INVALID_ARGUMENT, "Bind not allowed for default exchange"); } /* else if (!method.hasBindingKey()) @@ -735,9 +774,9 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "queue not set"); } - else if (!method.hasExchange()) + else if (nameNullOrEmpty(method.getExchange())) { - exception(session, method, ExecutionErrorCode.ILLEGAL_ARGUMENT, "exchange not set"); + exception(session, method, ExecutionErrorCode.INVALID_ARGUMENT, "Unbind not allowed for default exchange"); } else if (!method.hasBindingKey()) { @@ -767,9 +806,6 @@ public class ServerSessionDelegate extends SessionDelegate } } } - - - super.exchangeUnbind(session, method); } @Override @@ -969,10 +1005,10 @@ public class ServerSessionDelegate extends SessionDelegate } - if(method.hasAutoDelete() - && method.getAutoDelete() - && method.hasExclusive() - && method.getExclusive()) + if (method.hasAutoDelete() + && method.getAutoDelete() + && method.hasExclusive() + && method.getExclusive()) { final AMQQueue q = queue; final ServerSession.Task deleteQueueTask = new ServerSession.Task() @@ -999,23 +1035,23 @@ public class ServerSessionDelegate extends SessionDelegate } }); } - else if(method.getExclusive()) + if (method.hasExclusive() + && method.getExclusive()) { final AMQQueue q = queue; final ServerSession.Task removeExclusive = new ServerSession.Task() { - public void doTask(ServerSession session) { - q.setPrincipalHolder(null); + q.setAuthorizationHolder(null); q.setExclusiveOwningSession(null); } }; final ServerSession s = (ServerSession) session; + q.setExclusiveOwningSession(s); s.addSessionCloseTask(removeExclusive); queue.addQueueDeleteTask(new AMQQueue.Task() { - public void doTask(AMQQueue queue) throws AMQException { s.removeSessionCloseTask(removeExclusive); @@ -1029,7 +1065,7 @@ public class ServerSessionDelegate extends SessionDelegate } } } - else if (method.getExclusive() && (queue.getPrincipalHolder() != null && !queue.getPrincipalHolder().equals(session))) + else if (method.getExclusive() && (queue.getExclusiveOwningSession() != null && !queue.getExclusiveOwningSession().equals(session))) { String description = "Cannot declare queue('" + queueName + "')," + " as exclusive queue with same name " @@ -1077,7 +1113,7 @@ public class ServerSessionDelegate extends SessionDelegate } else { - if(queue.getPrincipalHolder() != null && queue.getPrincipalHolder() != session) + if(queue.getAuthorizationHolder() != null && queue.getAuthorizationHolder() != session) { exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); } @@ -1223,6 +1259,8 @@ public class ServerSessionDelegate extends SessionDelegate @Override public void closed(Session session) { + setThreadSubject(session); + for(Subscription_0_10 sub : getSubscriptions(session)) { ((ServerSession)session).unregister(sub); @@ -1241,4 +1279,9 @@ public class ServerSessionDelegate extends SessionDelegate return ((ServerSession)session).getSubscriptions(); } + private void setThreadSubject(Session session) + { + final ServerConnection scon = (ServerConnection) session.getConnection(); + SecurityManager.setThreadSubject(scon.getAuthorizedSubject()); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java index db781ead96..36e9d78440 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java @@ -50,6 +50,11 @@ public class AutoCommitTransaction implements ServerTransaction _transactionLog = transactionLog; } + public long getTransactionStartTime() + { + return 0L; + } + /** * Since AutoCommitTransaction have no concept of a long lived transaction, any Actions registered * by the caller are executed immediately. diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java index a04c743be1..946dbd7c28 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java @@ -20,18 +20,23 @@ package org.apache.qpid.server.txn; * */ - import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.server.message.EnqueableMessage; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.TransactionLog; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.store.TransactionLog; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A concrete implementation of ServerTransaction where enqueue/dequeue @@ -41,17 +46,28 @@ import org.apache.qpid.server.store.TransactionLog; */ public class LocalTransaction implements ServerTransaction { - protected static final Logger _logger = Logger.getLogger(LocalTransaction.class); + protected static final Logger _logger = LoggerFactory.getLogger(LocalTransaction.class); private final List<Action> _postTransactionActions = new ArrayList<Action>(); private volatile TransactionLog.Transaction _transaction; private TransactionLog _transactionLog; + private long _txnStartTime = 0L; public LocalTransaction(TransactionLog transactionLog) { _transactionLog = transactionLog; } + + public boolean inTransaction() + { + return _transaction != null; + } + + public long getTransactionStartTime() + { + return _txnStartTime; + } public void addPostTransactionAction(Action postTransactionAction) { @@ -89,7 +105,6 @@ public class LocalTransaction implements ServerTransaction try { - for(QueueEntry entry : queueEntries) { ServerMessage message = entry.getMessage(); @@ -113,7 +128,6 @@ public class LocalTransaction implements ServerTransaction _logger.error("Error during message dequeues", e); tidyUpOnError(e); } - } private void tidyUpOnError(Exception e) @@ -140,8 +154,7 @@ public class LocalTransaction implements ServerTransaction } finally { - _transaction = null; - _postTransactionActions.clear(); + resetDetails(); } } @@ -193,6 +206,11 @@ public class LocalTransaction implements ServerTransaction { _postTransactionActions.add(postTransactionAction); + if (_txnStartTime == 0L) + { + _txnStartTime = System.currentTimeMillis(); + } + if(message.isPersistent()) { try @@ -248,17 +266,14 @@ public class LocalTransaction implements ServerTransaction } finally { - _transaction = null; - _postTransactionActions.clear(); + resetDetails(); } - } public void rollback() { try { - if(_transaction != null) { _transaction.abortTran(); @@ -280,9 +295,15 @@ public class LocalTransaction implements ServerTransaction } finally { - _transaction = null; - _postTransactionActions.clear(); + resetDetails(); } } } + + private void resetDetails() + { + _transaction = null; + _postTransactionActions.clear(); + _txnStartTime = 0L; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java index b61b8a5c64..b3c6e1ac3a 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java @@ -52,6 +52,13 @@ public interface ServerTransaction public void onRollback(); } + /** + * Return the time the current transaction started. + * + * @return the time this transaction started or 0 if not in a transaction + */ + long getTransactionStartTime(); + /** * Register an Action for execution after transaction commit or rollback. Actions * will be executed in the order in which they are registered. diff --git a/java/broker/src/main/java/org/apache/qpid/server/util/ByteBufferInputStream.java b/java/broker/src/main/java/org/apache/qpid/server/util/ByteBufferInputStream.java new file mode 100644 index 0000000000..898a667736 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/util/ByteBufferInputStream.java @@ -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. + * + */ +package org.apache.qpid.server.util; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; + +public class ByteBufferInputStream extends InputStream +{ + private final ByteBuffer _buffer; + + public ByteBufferInputStream(ByteBuffer buffer) + { + _buffer = buffer; + } + + @Override + public int read() throws IOException + { + return _buffer.get() & 0xFF; + } + + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + if(_buffer.remaining() < len) + { + len = _buffer.remaining(); + } + _buffer.get(b, off, len); + + return len; + } + + @Override + public void mark(int readlimit) + { + _buffer.mark(); + } + + @Override + public void reset() throws IOException + { + _buffer.reset(); + } + + @Override + public boolean markSupported() + { + return true; + } + + @Override + public long skip(long n) throws IOException + { + + _buffer.position(_buffer.position()+(int)n); + + return n; + } + + @Override + public int available() throws IOException + { + return _buffer.remaining(); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java b/java/broker/src/main/java/org/apache/qpid/server/util/ByteBufferOutputStream.java index 9bd1e7c5e1..ca9a41bc32 100644 --- a/java/broker/src/test/java/org/apache/qpid/util/MockChannel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/util/ByteBufferOutputStream.java @@ -18,23 +18,29 @@ * under the License. * */ -package org.apache.qpid.util; +package org.apache.qpid.server.util; -import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.protocol.AMQProtocolSession; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; +import java.io.OutputStream; +import java.nio.ByteBuffer; -public class MockChannel extends AMQChannel +public class ByteBufferOutputStream extends OutputStream { - public MockChannel(AMQProtocolSession session, int channelId, MessageStore messageStore) - throws AMQException + private final ByteBuffer _buffer; + + public ByteBufferOutputStream(ByteBuffer buffer) { - super(session, channelId, messageStore); + _buffer = buffer; } + @Override + public void write(int b) + { + _buffer.put((byte)b); + } - + @Override + public void write(byte[] b, int off, int len) + { + _buffer.put(b, off, len); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/HouseKeepingTask.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/HouseKeepingTask.java index 2db1944cd1..ebace95f65 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/HouseKeepingTask.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/HouseKeepingTask.java @@ -63,6 +63,10 @@ public abstract class HouseKeepingTask implements Runnable { _logger.warn(this.getClass().getSimpleName() + " throw exception: " + e, e); } + finally + { + CurrentActor.remove(); + } } public VirtualHost getVirtualHost() diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index 4ed0507228..04f19b79bb 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -20,30 +20,28 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.UUID; + import org.apache.qpid.common.Closeable; +import org.apache.qpid.server.binding.BindingFactory; +import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.configuration.VirtualHostConfig; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.connection.IConnectionRegistry; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.configuration.VirtualHostConfiguration; -import org.apache.qpid.server.configuration.VirtualHostConfig; -import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.exchange.ExchangeRegistry; -import org.apache.qpid.server.exchange.ExchangeFactory; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.TransactionLog; -import org.apache.qpid.server.store.DurableConfigurationStore; +import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.binding.BindingFactory; - -import java.util.List; -import java.util.UUID; -import java.util.TimerTask; -import java.util.concurrent.FutureTask; +import org.apache.qpid.server.stats.StatisticsGatherer; +import org.apache.qpid.server.store.DurableConfigurationStore; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.TransactionLog; -public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHostConfig, Closeable +public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHostConfig, Closeable, StatisticsGatherer { IConnectionRegistry getConnectionRegistry(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java index 96a9ac729e..0fd31973b2 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java @@ -43,7 +43,10 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.AMQException; import org.apache.log4j.Logger; +import org.apache.qpid.server.util.ByteBufferInputStream; +import java.io.DataInputStream; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; @@ -236,7 +239,14 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa FieldTable argumentsFT = null; if(buf != null) { - argumentsFT = new FieldTable(org.apache.mina.common.ByteBuffer.wrap(buf),buf.limit()); + try + { + argumentsFT = new FieldTable(new DataInputStream(new ByteBufferInputStream(buf)),buf.limit()); + } + catch (IOException e) + { + throw new RuntimeException("IOException should not be thrown here", e); + } } BindingFactory bf = _virtualHost.getBindingFactory(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java index 6ec1c512e5..17c65003e9 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java @@ -20,23 +20,21 @@ */ package org.apache.qpid.server.virtualhost; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQStoreException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -63,6 +61,8 @@ import org.apache.qpid.server.logging.messages.VirtualHostMessages; import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.DefaultQueueRegistry; @@ -71,7 +71,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.store.ConfigurationRecoveryHandler; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; @@ -99,7 +99,7 @@ public class VirtualHostImpl implements VirtualHost private AMQBrokerManagerMBean _brokerMBean; - private AuthenticationManager _authenticationManager; + private final AuthenticationManager _authenticationManager; private SecurityManager _securityManager; @@ -111,6 +111,8 @@ public class VirtualHostImpl implements VirtualHost private BrokerConfig _broker; private UUID _id; + private boolean _statisticsEnabled = false; + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; private final long _createTime = System.currentTimeMillis(); private final ConcurrentHashMap<BrokerLink,BrokerLink> _links = new ConcurrentHashMap<BrokerLink, BrokerLink>(); @@ -161,12 +163,12 @@ public class VirtualHostImpl implements VirtualHost public String getObjectInstanceName() { - return _name.toString(); + return ObjectName.quote(_name); } public String getName() { - return _name.toString(); + return _name; } public VirtualHostImpl getVirtualHost() @@ -175,22 +177,11 @@ public class VirtualHostImpl implements VirtualHost } } - public VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig) throws Exception - { - this(appRegistry, hostConfig, null); - } - - - public VirtualHostImpl(VirtualHostConfiguration hostConfig, MessageStore store) throws Exception - { - this(ApplicationRegistry.getInstance(),hostConfig,store); - } - - private VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig, MessageStore store) throws Exception + public VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig, MessageStore store) throws Exception { if (hostConfig == null) { - throw new IllegalAccessException("HostConfig and MessageStore cannot be null"); + throw new IllegalArgumentException("HostConfig cannot be null"); } _appRegistry = appRegistry; @@ -244,21 +235,28 @@ public class VirtualHostImpl implements VirtualHost initialiseMessageStore(hostConfig); } - _authenticationManager = new PrincipalDatabaseAuthenticationManager(_name, _configuration); + _authenticationManager = ApplicationRegistry.getInstance().getAuthenticationManager(); _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); _brokerMBean.register(); - initialiseHouseKeeping(hostConfig.getHousekeepingExpiredMessageCheckPeriod()); + initialiseHouseKeeping(hostConfig.getHousekeepingCheckPeriod()); + + initialiseStatistics(); } + /** + * Initialise a housekeeping task to iterate over queues cleaning expired messages with no consumers + * and checking for idle or open transactions that have exceeded the permitted thresholds. + * + * @param period + */ private void initialiseHouseKeeping(long period) { - /* add a timer task to iterate over queues, cleaning expired messages from queues with no consumers */ if (period != 0L) { - class ExpiredMessagesTask extends HouseKeepingTask + class VirtualHostHouseKeepingTask extends HouseKeepingTask { - public ExpiredMessagesTask(VirtualHost vhost) + public VirtualHostHouseKeepingTask(VirtualHost vhost) { super(vhost); } @@ -281,18 +279,29 @@ public class VirtualHostImpl implements VirtualHost // house keeping task from running. } } + for (AMQConnectionModel connection : getConnectionRegistry().getConnections()) + { + _logger.debug("Checking for long running open transactions on connection " + connection); + for (AMQSessionModel session : connection.getSessionModels()) + { + _logger.debug("Checking for long running open transactions on session " + session); + try + { + session.checkTransactionStatus(_configuration.getTransactionTimeoutOpenWarn(), + _configuration.getTransactionTimeoutOpenClose(), + _configuration.getTransactionTimeoutIdleWarn(), + _configuration.getTransactionTimeoutIdleClose()); + } + catch (Exception e) + { + _logger.error("Exception in housekeeping for connection: " + connection.toString(), e); + } + } + } } } - scheduleHouseKeepingTask(period, new ExpiredMessagesTask(this)); - - class ForceChannelClosuresTask extends TimerTask - { - public void run() - { - _connectionRegistry.expireClosedChannels(); - } - } + scheduleHouseKeepingTask(period, new VirtualHostHouseKeepingTask(this)); Map<String, VirtualHostPluginFactory> plugins = ApplicationRegistry.getInstance().getPluginManager().getVirtualHostPlugins(); @@ -446,46 +455,57 @@ public class VirtualHostImpl implements VirtualHost private void configureQueue(QueueConfiguration queueConfiguration) throws AMQException, ConfigurationException { AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueConfiguration, this); + String queueName = queue.getName(); if (queue.isDurable()) { getDurableConfigurationStore().createQueue(queue); } + //get the exchange name (returns default exchange name if none was specified) String exchangeName = queueConfiguration.getExchange(); - Exchange exchange = _exchangeRegistry.getExchange(exchangeName == null ? null : new AMQShortString(exchangeName)); - - if (exchange == null) - { - exchange = _exchangeRegistry.getDefaultExchange(); - } - + Exchange exchange = _exchangeRegistry.getExchange(exchangeName); if (exchange == null) { - throw new ConfigurationException("Attempt to bind queue to unknown exchange:" + exchangeName); + throw new ConfigurationException("Attempt to bind queue '" + queueName + "' to unknown exchange:" + exchangeName); } - List routingKeys = queueConfiguration.getRoutingKeys(); - if (routingKeys == null || routingKeys.isEmpty()) - { - routingKeys = Collections.singletonList(queue.getNameShortString()); - } + Exchange defaultExchange = _exchangeRegistry.getDefaultExchange(); + + //get routing keys in configuration (returns empty list if none are defined) + List<?> routingKeys = queueConfiguration.getRoutingKeys(); for (Object routingKeyNameObj : routingKeys) { - AMQShortString routingKey = new AMQShortString(String.valueOf(routingKeyNameObj)); - if (_logger.isInfoEnabled()) + String routingKey = String.valueOf(routingKeyNameObj); + + if (exchange.equals(defaultExchange) && !queueName.equals(routingKey)) { - _logger.info("Binding queue:" + queue + " with routing key '" + routingKey + "' to exchange:" + this); + throw new ConfigurationException("Illegal attempt to bind queue '" + queueName + + "' to the default exchange with a key other than the queue name: " + routingKey); } - _bindingFactory.addBinding(routingKey.toString(), queue, exchange, null); + + configureBinding(queue, exchange, routingKey); + } + + if (!exchange.equals(defaultExchange)) + { + //bind the queue to the named exchange using its name + configureBinding(queue, exchange, queueName); } - if (exchange != _exchangeRegistry.getDefaultExchange()) + //ensure the queue is bound to the default exchange using its name + configureBinding(queue, defaultExchange, queueName); + } + + private void configureBinding(AMQQueue queue, Exchange exchange, String routingKey) throws AMQException + { + if (_logger.isInfoEnabled()) { - _bindingFactory.addBinding(queue.getNameShortString().toString(), queue, exchange, null); + _logger.info("Binding queue:" + queue + " with routing key '" + routingKey + "' to exchange:" + exchange.getName()); } + _bindingFactory.addBinding(routingKey, queue, exchange, null); } public String getName() @@ -627,6 +647,80 @@ public class VirtualHostImpl implements VirtualHost { return _bindingFactory; } + + public void registerMessageDelivered(long messageSize) + { + if (isStatisticsEnabled()) + { + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); + } + _appRegistry.registerMessageDelivered(messageSize); + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + if (isStatisticsEnabled()) + { + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); + } + _appRegistry.registerMessageReceived(messageSize, timestamp); + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return _messagesReceived; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return _dataReceived; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return _messagesDelivered; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return _dataDelivered; + } + + public void resetStatistics() + { + _messagesDelivered.reset(); + _dataDelivered.reset(); + _messagesReceived.reset(); + _dataReceived.reset(); + + for (AMQConnectionModel connection : _connectionRegistry.getConnections()) + { + connection.resetStatistics(); + } + } + + public void initialiseStatistics() + { + setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && + _appRegistry.getConfiguration().isStatisticsGenerationVirtualhostsEnabled()); + + _messagesDelivered = new StatisticsCounter("messages-delivered-" + getName()); + _dataDelivered = new StatisticsCounter("bytes-delivered-" + getName()); + _messagesReceived = new StatisticsCounter("messages-received-" + getName()); + _dataReceived = new StatisticsCounter("bytes-received-" + getName()); + } + + public boolean isStatisticsEnabled() + { + return _statisticsEnabled; + } + + public void setStatisticsEnabled(boolean enabled) + { + _statisticsEnabled = enabled; + } public void createBrokerConnection(final String transport, final String host, diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java index dca165fa7e..2c0ceed80b 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java @@ -192,7 +192,7 @@ public class MessageStoreTool if (_initialised) { - ApplicationRegistry.remove(1); + ApplicationRegistry.remove(); } _console.println("...exiting"); @@ -274,7 +274,7 @@ public class MessageStoreTool { ConfigurationFileApplicationRegistry registry = new ConfigurationFileApplicationRegistry(configFile); - ApplicationRegistry.remove(1); + ApplicationRegistry.remove(); ApplicationRegistry.initialise(registry); diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java index 4fd4999b19..806e161bbc 100644 --- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java +++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java @@ -364,7 +364,7 @@ public class Show extends AbstractCommand { if(msg instanceof AMQMessage) { - headers = ((BasicContentHeaderProperties) ((AMQMessage)msg).getContentHeaderBody().properties); + headers = ((BasicContentHeaderProperties) ((AMQMessage)msg).getContentHeaderBody().getProperties()); } } catch (AMQException e) diff --git a/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java b/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java new file mode 100644 index 0000000000..131f316330 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java @@ -0,0 +1,202 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.apache.qpid.test.utils.QpidTestCase; + + +public class BrokerOptionsTest extends QpidTestCase +{ + private BrokerOptions _options; + + private static final int TEST_PORT1 = 6789; + private static final int TEST_PORT2 = 6790; + + + protected void setUp() + { + _options = new BrokerOptions(); + } + + public void testDefaultPort() + { + assertEquals(Collections.<Integer>emptySet(), _options.getPorts()); + } + + public void testOverriddenPort() + { + _options.addPort(TEST_PORT1); + assertEquals(Collections.singleton(TEST_PORT1), _options.getPorts()); + } + + public void testManyOverriddenPorts() + { + _options.addPort(TEST_PORT1); + _options.addPort(TEST_PORT2); + final Set<Integer> expectedPorts = new HashSet<Integer>(Arrays.asList(new Integer[] {TEST_PORT1, TEST_PORT2})); + assertEquals(expectedPorts, _options.getPorts()); + } + + public void testDuplicateOverriddenPortsAreSilentlyIgnored() + { + _options.addPort(TEST_PORT1); + _options.addPort(TEST_PORT2); + _options.addPort(TEST_PORT1); // duplicate - should be silently ignored + final Set<Integer> expectedPorts = new HashSet<Integer>(Arrays.asList(new Integer[] {TEST_PORT1, TEST_PORT2})); + assertEquals(expectedPorts, _options.getPorts()); + } + + public void testDefaultSSLPort() + { + assertEquals(Collections.<Integer>emptySet(), _options.getSSLPorts()); + } + + public void testOverriddenSSLPort() + { + _options.addSSLPort(TEST_PORT1); + assertEquals(Collections.singleton(TEST_PORT1), _options.getSSLPorts()); + } + + public void testManyOverriddenSSLPorts() + { + _options.addSSLPort(TEST_PORT1); + _options.addSSLPort(TEST_PORT2); + final Set<Integer> expectedPorts = new HashSet<Integer>(Arrays.asList(new Integer[] {TEST_PORT1, TEST_PORT2})); + assertEquals(expectedPorts, _options.getSSLPorts()); + } + + public void testDuplicateOverriddenSSLPortsAreSilentlyIgnored() + { + _options.addSSLPort(TEST_PORT1); + _options.addSSLPort(TEST_PORT2); + _options.addSSLPort(TEST_PORT1); // duplicate - should be silently ignored + final Set<Integer> expectedPorts = new HashSet<Integer>(Arrays.asList(new Integer[] {TEST_PORT1, TEST_PORT2})); + assertEquals(expectedPorts, _options.getSSLPorts()); + } + + public void testDefaultConfigFile() + { + assertNull(_options.getConfigFile()); + } + + public void testOverriddenConfigFile() + { + final String testConfigFile = "etc/mytestconfig.xml"; + _options.setConfigFile(testConfigFile); + assertEquals(testConfigFile, _options.getConfigFile()); + } + + public void testDefaultLogConfigFile() + { + assertNull(_options.getLogConfigFile()); + } + + public void testOverriddenLogConfigFile() + { + final String testLogConfigFile = "etc/mytestlog4j.xml"; + _options.setLogConfigFile(testLogConfigFile); + assertEquals(testLogConfigFile, _options.getLogConfigFile()); + } + + public void testDefaultJmxPortRegistryServer() + { + assertNull(_options.getJmxPortRegistryServer()); + } + + public void testJmxPortRegistryServer() + { + _options.setJmxPortRegistryServer(TEST_PORT1); + assertEquals(Integer.valueOf(TEST_PORT1), _options.getJmxPortRegistryServer()); + } + + public void testDefaultJmxPortConnectorServer() + { + assertNull(_options.getJmxPortConnectorServer()); + } + + public void testJmxPortConnectorServer() + { + _options.setJmxPortConnectorServer(TEST_PORT1); + assertEquals(Integer.valueOf(TEST_PORT1), _options.getJmxPortConnectorServer()); + } + + public void testQpidHomeExposesSysProperty() + { + assertEquals(System.getProperty("QPID_HOME"), _options.getQpidHome()); + } + + public void testDefaultExcludesPortFor0_10() + { + assertEquals(Collections.EMPTY_SET, _options.getExcludedPorts(ProtocolExclusion.v0_10)); + } + + public void testOverriddenExcludesPortFor0_10() + { + _options.addExcludedPort(ProtocolExclusion.v0_10, TEST_PORT1); + assertEquals(Collections.singleton(TEST_PORT1), _options.getExcludedPorts(ProtocolExclusion.v0_10)); + } + + public void testManyOverriddenExcludedPortFor0_10() + { + _options.addExcludedPort(ProtocolExclusion.v0_10, TEST_PORT1); + _options.addExcludedPort(ProtocolExclusion.v0_10, TEST_PORT2); + final Set<Integer> expectedPorts = new HashSet<Integer>(Arrays.asList(new Integer[] {TEST_PORT1, TEST_PORT2})); + assertEquals(expectedPorts, _options.getExcludedPorts(ProtocolExclusion.v0_10)); + } + + public void testDuplicatedOverriddenExcludedPortFor0_10AreSilentlyIgnored() + { + _options.addExcludedPort(ProtocolExclusion.v0_10, TEST_PORT1); + _options.addExcludedPort(ProtocolExclusion.v0_10, TEST_PORT2); + final Set<Integer> expectedPorts = new HashSet<Integer>(Arrays.asList(new Integer[] {TEST_PORT1, TEST_PORT2})); + assertEquals(expectedPorts, _options.getExcludedPorts(ProtocolExclusion.v0_10)); + } + + public void testDefaultBind() + { + assertNull(_options.getBind()); + } + + public void testOverriddenBind() + { + final String bind = "192.168.0.1"; + _options.setBind(bind); + assertEquals(bind, _options.getBind()); + } + + public void testDefaultLogWatchFrequency() + { + assertEquals(0L, _options.getLogWatchFrequency()); + } + + public void testOverridenLogWatchFrequency() + { + final int myFreq = 10 * 1000; + + _options.setLogWatchFrequency(myFreq); + assertEquals(myFreq, _options.getLogWatchFrequency()); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/MainTest.java b/java/broker/src/test/java/org/apache/qpid/server/MainTest.java new file mode 100644 index 0000000000..9b0ae82b84 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/MainTest.java @@ -0,0 +1,153 @@ +package org.apache.qpid.server; + +import java.util.EnumSet; + +import org.apache.qpid.test.utils.QpidTestCase; + +/** + * Test to verify the command line parsing within the Main class, by + * providing it a series of command line arguments and verifying the + * BrokerOptions emerging for use in starting the Broker instance. + */ +public class MainTest extends QpidTestCase +{ + public void testNoOptionsSpecified() + { + BrokerOptions options = startDummyMain(""); + + assertTrue(options.getPorts().isEmpty()); + assertTrue(options.getSSLPorts().isEmpty()); + assertEquals(null, options.getJmxPortRegistryServer()); + assertEquals(null, options.getConfigFile()); + assertEquals(null, options.getLogConfigFile()); + assertEquals(null, options.getBind()); + + for(ProtocolExclusion pe : EnumSet.allOf(ProtocolExclusion.class)) + { + assertEquals(0, options.getExcludedPorts(pe).size()); + } + } + + public void testPortOverriddenSingle() + { + BrokerOptions options = startDummyMain("-p 1234"); + + assertTrue(options.getPorts().contains(1234)); + assertEquals(1, options.getPorts().size()); + assertTrue(options.getSSLPorts().isEmpty()); + } + + public void testPortOverriddenMultiple() + { + BrokerOptions options = startDummyMain("-p 1234 -p 4321"); + + assertTrue(options.getPorts().contains(1234)); + assertTrue(options.getPorts().contains(4321)); + assertEquals(2, options.getPorts().size()); + assertTrue(options.getSSLPorts().isEmpty()); + } + + public void testSSLPortOverriddenSingle() + { + BrokerOptions options = startDummyMain("-s 5678"); + + assertTrue(options.getSSLPorts().contains(5678)); + assertEquals(1, options.getSSLPorts().size()); + assertTrue(options.getPorts().isEmpty()); + } + + public void testSSLPortOverriddenMultiple() + { + BrokerOptions options = startDummyMain("-s 5678 -s 8765"); + + assertTrue(options.getSSLPorts().contains(5678)); + assertTrue(options.getSSLPorts().contains(8765)); + assertEquals(2, options.getSSLPorts().size()); + assertTrue(options.getPorts().isEmpty()); + } + + public void testNonSSLandSSLPortsOverridden() + { + BrokerOptions options = startDummyMain("-p 5678 -s 8765"); + + assertTrue(options.getPorts().contains(5678)); + assertTrue(options.getSSLPorts().contains(8765)); + assertEquals(1, options.getPorts().size()); + assertEquals(1, options.getSSLPorts().size()); + } + + public void testJmxPortRegistryServerOverridden() + { + BrokerOptions options = startDummyMain("--jmxregistryport 3456"); + + assertEquals(Integer.valueOf(3456), options.getJmxPortRegistryServer()); + + options = startDummyMain("-m 3457"); + assertEquals(Integer.valueOf(3457), options.getJmxPortRegistryServer()); + } + + public void testJmxPortConnectorServerOverridden() + { + BrokerOptions options = startDummyMain("--jmxconnectorport 3456"); + + assertEquals(Integer.valueOf(3456), options.getJmxPortConnectorServer()); + } + + public void testExclude0_10() + { + BrokerOptions options = startDummyMain("-p 3456 --exclude-0-10 3456"); + + assertTrue(options.getPorts().contains(3456)); + assertEquals(1, options.getPorts().size()); + assertTrue(options.getExcludedPorts(ProtocolExclusion.v0_10).contains(3456)); + assertEquals(1, options.getExcludedPorts(ProtocolExclusion.v0_10).size()); + assertEquals(0, options.getExcludedPorts(ProtocolExclusion.v0_9_1).size()); + } + + public void testConfig() + { + BrokerOptions options = startDummyMain("-c abcd/config.xml"); + + assertEquals("abcd/config.xml", options.getConfigFile()); + } + + public void testLogConfig() + { + BrokerOptions options = startDummyMain("-l wxyz/log4j.xml"); + + assertEquals("wxyz/log4j.xml", options.getLogConfigFile()); + } + + public void testLogWatch() + { + BrokerOptions options = startDummyMain("-w 9"); + + assertEquals(9, options.getLogWatchFrequency()); + } + + private BrokerOptions startDummyMain(String commandLine) + { + return (new TestMain(commandLine.split("\\s"))).getOptions(); + } + + private class TestMain extends Main + { + private BrokerOptions _options; + + public TestMain(String[] args) + { + super(args); + } + + @Override + protected void startBroker(BrokerOptions options) + { + _options = options; + } + + public BrokerOptions getOptions() + { + return _options; + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java b/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java deleted file mode 100644 index 59543874b4..0000000000 --- a/java/broker/src/test/java/org/apache/qpid/server/RunBrokerWithCommand.java +++ /dev/null @@ -1,132 +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; - -import org.apache.log4j.Logger; -import org.apache.log4j.Level; - -import java.io.InputStream; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.IOException; - -public class RunBrokerWithCommand -{ - public static void main(String[] args) - { - //Start the broker - try - { - String[] fudge = args.clone(); - - // Override the first value which is the command we are going to run later. - fudge[0] = "-v"; - new Main(fudge).startup(); - } - catch (Exception e) - { - System.err.println("Unable to start broker due to: " + e.getMessage()); - - e.printStackTrace(); - exit(1); - } - - Logger.getRootLogger().setLevel(Level.ERROR); - - //run command - try - { - Process task = Runtime.getRuntime().exec(args[0]); - System.err.println("Started Proccess: " + args[0]); - - InputStream inputStream = task.getInputStream(); - - InputStream errorStream = task.getErrorStream(); - - Thread out = new Thread(new Outputter("[OUT]", new BufferedReader(new InputStreamReader(inputStream)))); - Thread err = new Thread(new Outputter("[ERR]", new BufferedReader(new InputStreamReader(errorStream)))); - - out.start(); - err.start(); - - out.join(); - err.join(); - - System.err.println("Waiting for process to exit: " + args[0]); - task.waitFor(); - System.err.println("Done Proccess: " + args[0]); - - } - catch (IOException e) - { - System.err.println("Proccess had problems: " + e.getMessage()); - e.printStackTrace(System.err); - exit(1); - } - catch (InterruptedException e) - { - System.err.println("Proccess had problems: " + e.getMessage()); - e.printStackTrace(System.err); - - exit(1); - } - - - exit(0); - } - - private static void exit(int i) - { - Logger.getRootLogger().setLevel(Level.INFO); - System.exit(i); - } - - static class Outputter implements Runnable - { - - BufferedReader reader; - String prefix; - - Outputter(String s, BufferedReader r) - { - prefix = s; - reader = r; - } - - public void run() - { - String line; - try - { - while ((line = reader.readLine()) != null) - { - System.out.println(prefix + line); - } - } - catch (IOException e) - { - System.out.println("Error occured reading; " + e.getMessage()); - } - } - - } - -} diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java index 718874cf69..d22f1e6e94 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java @@ -20,742 +20,598 @@ */ package org.apache.qpid.server.configuration; +import static org.apache.qpid.transport.ConnectionSettings.WILDCARD_ADDRESS; + import java.io.File; import java.io.FileWriter; import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.List; import java.util.Locale; -import junit.framework.TestCase; - import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.protocol.AMQProtocolEngine; -import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry; -import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.util.TestApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -import org.apache.qpid.transport.TestNetworkDriver; +import org.apache.qpid.test.utils.QpidTestCase; -public class ServerConfigurationTest extends InternalBrokerBaseCase +public class ServerConfigurationTest extends QpidTestCase { private XMLConfiguration _config = new XMLConfiguration(); + private ServerConfiguration _serverConfig = null; - - public void testSetJMXManagementPort() throws ConfigurationException + @Override + protected void setUp() throws Exception { - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - serverConfig.setJMXManagementPort(23); - assertEquals(23, serverConfig.getJMXManagementPort()); + super.setUp(); + _serverConfig = new ServerConfiguration(_config); + ApplicationRegistry.initialise(new TestApplicationRegistry(_serverConfig)); } - public void testGetJMXManagementPort() throws ConfigurationException + @Override + protected void tearDown() throws Exception { - _config.setProperty("management.jmxport", 42); - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(42, serverConfig.getJMXManagementPort()); + super.tearDown(); + ApplicationRegistry.remove(); } - public void testGetPlatformMbeanserver() throws ConfigurationException + public void testSetJMXPortRegistryServer() throws ConfigurationException { - // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getPlatformMbeanserver()); - - // Check value we set - _config.setProperty("management.platform-mbeanserver", false); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getPlatformMbeanserver()); + _serverConfig.initialise(); + _serverConfig.setJMXPortRegistryServer(23); + assertEquals(23, _serverConfig.getJMXPortRegistryServer()); } - public void testGetPluginDirectory() throws ConfigurationException + public void testGetJMXPortRegistryServer() throws ConfigurationException { - // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(null, serverConfig.getPluginDirectory()); - - // Check value we set - _config.setProperty("plugin-directory", "/path/to/plugins"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("/path/to/plugins", serverConfig.getPluginDirectory()); + _config.setProperty(ServerConfiguration.MGMT_JMXPORT_REGISTRYSERVER, 42); + _serverConfig.initialise(); + assertEquals(42, _serverConfig.getJMXPortRegistryServer()); } - public void testGetCacheDirectory() throws ConfigurationException + public void testDefaultJMXPortRegistryServer() throws ConfigurationException { - // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(null, serverConfig.getCacheDirectory()); - - // Check value we set - _config.setProperty("cache-directory", "/path/to/cache"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("/path/to/cache", serverConfig.getCacheDirectory()); + _serverConfig.initialise(); + assertEquals(8999, _serverConfig.getJMXPortRegistryServer()); } - public void testGetPrincipalDatabaseNames() throws ConfigurationException + public void testSetJMXPortConnectorServer() throws ConfigurationException { - // Check default ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getPrincipalDatabaseNames().size()); - - // Check value we set - _config.setProperty("security.principal-databases.principal-database(0).name", "a"); - _config.setProperty("security.principal-databases.principal-database(1).name", "b"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - List<String> dbs = serverConfig.getPrincipalDatabaseNames(); - assertEquals(2, dbs.size()); - assertEquals("a", dbs.get(0)); - assertEquals("b", dbs.get(1)); + serverConfig.setJMXPortConnectorServer(67); + assertEquals(67, serverConfig.getJMXConnectorServerPort()); } - public void testGetPrincipalDatabaseClass() throws ConfigurationException + public void testGetJMXPortConnectorServer() throws ConfigurationException { - // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getPrincipalDatabaseClass().size()); - - // Check value we set - _config.setProperty("security.principal-databases.principal-database(0).class", "a"); - _config.setProperty("security.principal-databases.principal-database(1).class", "b"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - List<String> dbs = serverConfig.getPrincipalDatabaseClass(); - assertEquals(2, dbs.size()); - assertEquals("a", dbs.get(0)); - assertEquals("b", dbs.get(1)); - } - - public void testGetPrincipalDatabaseAttributeNames() throws ConfigurationException - { - // Check default + _config.setProperty(ServerConfiguration.MGMT_JMXPORT_CONNECTORSERVER, 67); ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getPrincipalDatabaseAttributeNames(1).size()); - - // Check value we set - _config.setProperty("security.principal-databases.principal-database(0).attributes(0).attribute.name", "a"); - _config.setProperty("security.principal-databases.principal-database(0).attributes(1).attribute.name", "b"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - List<String> dbs = serverConfig.getPrincipalDatabaseAttributeNames(0); - assertEquals(2, dbs.size()); - assertEquals("a", dbs.get(0)); - assertEquals("b", dbs.get(1)); + assertEquals(67, serverConfig.getJMXConnectorServerPort()); } - public void testGetPrincipalDatabaseAttributeValues() throws ConfigurationException + public void testDefaultJMXPortConnectorServer() throws ConfigurationException { - // Check default ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getPrincipalDatabaseAttributeValues(1).size()); - - // Check value we set - _config.setProperty("security.principal-databases.principal-database(0).attributes(0).attribute.value", "a"); - _config.setProperty("security.principal-databases.principal-database(0).attributes(1).attribute.value", "b"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - List<String> dbs = serverConfig.getPrincipalDatabaseAttributeValues(0); - assertEquals(2, dbs.size()); - assertEquals("a", dbs.get(0)); - assertEquals("b", dbs.get(1)); + assertEquals(ServerConfiguration.DEFAULT_JMXPORT_REGISTRYSERVER + ServerConfiguration.JMXPORT_CONNECTORSERVER_OFFSET, + serverConfig.getJMXConnectorServerPort()); } - public void testGetManagementAccessList() throws ConfigurationException - { - // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getManagementAccessList().size()); - - // Check value we set - _config.setProperty("security.jmx.access(0)", "a"); - _config.setProperty("security.jmx.access(1)", "b"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - List<String> dbs = serverConfig.getManagementAccessList(); - assertEquals(2, dbs.size()); - assertEquals("a", dbs.get(0)); - assertEquals("b", dbs.get(1)); - } - - public void testGetFrameSize() throws ConfigurationException + public void testGetPlatformMbeanserver() throws ConfigurationException { - // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(65536, serverConfig.getFrameSize()); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getPlatformMbeanserver()); // Check value we set - _config.setProperty("advanced.framesize", "23"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(23, serverConfig.getFrameSize()); + _config.setProperty("management.platform-mbeanserver", false); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getPlatformMbeanserver()); } - public void testGetProtectIOEnabled() throws ConfigurationException + public void testGetPluginDirectory() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getProtectIOEnabled()); + _serverConfig.initialise(); + assertEquals(null, _serverConfig.getPluginDirectory()); // Check value we set - _config.setProperty(ServerConfiguration.CONNECTOR_PROTECTIO_ENABLED, true); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getProtectIOEnabled()); + _config.setProperty("plugin-directory", "/path/to/plugins"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("/path/to/plugins", _serverConfig.getPluginDirectory()); } - public void testGetBufferReadLimit() throws ConfigurationException + public void testGetCacheDirectory() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(262144, serverConfig.getBufferReadLimit()); + _serverConfig.initialise(); + assertEquals(null, _serverConfig.getCacheDirectory()); // Check value we set - _config.setProperty(ServerConfiguration.CONNECTOR_PROTECTIO_READ_BUFFER_LIMIT_SIZE, 23); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(23, serverConfig.getBufferReadLimit()); + _config.setProperty("cache-directory", "/path/to/cache"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("/path/to/cache", _serverConfig.getCacheDirectory()); } - public void testGetBufferWriteLimit() throws ConfigurationException + public void testGetFrameSize() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(262144, serverConfig.getBufferWriteLimit()); + _serverConfig.initialise(); + assertEquals(65536, _serverConfig.getFrameSize()); // Check value we set - _config.setProperty(ServerConfiguration.CONNECTOR_PROTECTIO_WRITE_BUFFER_LIMIT_SIZE, 23); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(23, serverConfig.getBufferWriteLimit()); + _config.setProperty("advanced.framesize", "23"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(23, _serverConfig.getFrameSize()); } - public void testGetStatusEnabled() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); + _serverConfig.initialise(); assertEquals(ServerConfiguration.DEFAULT_STATUS_UPDATES.equalsIgnoreCase("on"), - serverConfig.getStatusUpdatesEnabled()); + _serverConfig.getStatusUpdatesEnabled()); // Check disabling we set _config.setProperty(ServerConfiguration.STATUS_UPDATES, "off"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getStatusUpdatesEnabled()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getStatusUpdatesEnabled()); // Check invalid values don't cause error but result in disabled _config.setProperty(ServerConfiguration.STATUS_UPDATES, "Yes Please"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getStatusUpdatesEnabled()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getStatusUpdatesEnabled()); } public void testGetSynchedClocks() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getSynchedClocks()); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getSynchedClocks()); // Check value we set _config.setProperty("advanced.synced-clocks", true); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getSynchedClocks()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getSynchedClocks()); } public void testGetLocale() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); + _serverConfig.initialise(); // The Default is what ever the VMs default is Locale defaultLocale = Locale.getDefault(); - assertEquals(defaultLocale, serverConfig.getLocale()); + assertEquals(defaultLocale, _serverConfig.getLocale()); //Test Language only Locale update = new Locale("es"); _config.setProperty(ServerConfiguration.ADVANCED_LOCALE, "es"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(update, serverConfig.getLocale()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(update, _serverConfig.getLocale()); //Test Language and Country update = new Locale("es","ES"); _config.setProperty(ServerConfiguration.ADVANCED_LOCALE, "es_ES"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(update, serverConfig.getLocale()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(update, _serverConfig.getLocale()); //Test Language and Country and Variant update = new Locale("es","ES", "Traditional_WIN"); _config.setProperty(ServerConfiguration.ADVANCED_LOCALE, "es_ES_Traditional_WIN"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(update, serverConfig.getLocale()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(update, _serverConfig.getLocale()); } public void testGetMsgAuth() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getMsgAuth()); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getMsgAuth()); // Check value we set _config.setProperty("security.msg-auth", true); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getMsgAuth()); - } - - public void testGetJMXPrincipalDatabase() throws ConfigurationException - { - // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(null, serverConfig.getJMXPrincipalDatabase()); - - // Check value we set - _config.setProperty("security.jmx.principal-database", "a"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("a", serverConfig.getJMXPrincipalDatabase()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getMsgAuth()); } public void testGetManagementKeyStorePath() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(null, serverConfig.getManagementKeyStorePath()); + _serverConfig.initialise(); + assertEquals(null, _serverConfig.getManagementKeyStorePath()); // Check value we set _config.setProperty("management.ssl.keyStorePath", "a"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("a", serverConfig.getManagementKeyStorePath()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("a", _serverConfig.getManagementKeyStorePath()); } public void testGetManagementSSLEnabled() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getManagementSSLEnabled()); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getManagementSSLEnabled()); // Check value we set _config.setProperty("management.ssl.enabled", false); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getManagementSSLEnabled()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getManagementSSLEnabled()); } - public void testGetManagementKeyStorePassword() throws ConfigurationException + public void testGetManagementKeystorePassword() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(null, serverConfig.getManagementKeyStorePassword()); + _serverConfig.initialise(); + assertEquals(null, _serverConfig.getManagementKeyStorePassword()); // Check value we set _config.setProperty("management.ssl.keyStorePassword", "a"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("a", serverConfig.getManagementKeyStorePassword()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("a", _serverConfig.getManagementKeyStorePassword()); } public void testGetQueueAutoRegister() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getQueueAutoRegister()); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getQueueAutoRegister()); // Check value we set _config.setProperty("queue.auto_register", false); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getQueueAutoRegister()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getQueueAutoRegister()); } public void testGetManagementEnabled() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getManagementEnabled()); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getManagementEnabled()); // Check value we set _config.setProperty("management.enabled", false); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getManagementEnabled()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getManagementEnabled()); } public void testSetManagementEnabled() throws ConfigurationException { // Check value we set - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - serverConfig.setManagementEnabled(false); - assertEquals(false, serverConfig.getManagementEnabled()); + _serverConfig.initialise(); + _serverConfig.setManagementEnabled(false); + assertEquals(false, _serverConfig.getManagementEnabled()); } public void testGetHeartBeatDelay() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(5, serverConfig.getHeartBeatDelay()); + _serverConfig.initialise(); + assertEquals(5, _serverConfig.getHeartBeatDelay()); // Check value we set _config.setProperty("heartbeat.delay", 23); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(23, serverConfig.getHeartBeatDelay()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(23, _serverConfig.getHeartBeatDelay()); } public void testGetHeartBeatTimeout() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(2.0, serverConfig.getHeartBeatTimeout()); + _serverConfig.initialise(); + assertEquals(2.0, _serverConfig.getHeartBeatTimeout()); // Check value we set _config.setProperty("heartbeat.timeoutFactor", 2.3); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(2.3, serverConfig.getHeartBeatTimeout()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(2.3, _serverConfig.getHeartBeatTimeout()); } public void testGetMaximumMessageAge() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getMaximumMessageAge()); + _serverConfig.initialise(); + assertEquals(0, _serverConfig.getMaximumMessageAge()); // Check value we set _config.setProperty("maximumMessageAge", 10L); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(10, serverConfig.getMaximumMessageAge()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(10, _serverConfig.getMaximumMessageAge()); } public void testGetMaximumMessageCount() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getMaximumMessageCount()); + _serverConfig.initialise(); + assertEquals(0, _serverConfig.getMaximumMessageCount()); // Check value we set _config.setProperty("maximumMessageCount", 10L); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(10, serverConfig.getMaximumMessageCount()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(10, _serverConfig.getMaximumMessageCount()); } public void testGetMaximumQueueDepth() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getMaximumQueueDepth()); + _serverConfig.initialise(); + assertEquals(0, _serverConfig.getMaximumQueueDepth()); // Check value we set _config.setProperty("maximumQueueDepth", 10L); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(10, serverConfig.getMaximumQueueDepth()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(10, _serverConfig.getMaximumQueueDepth()); } public void testGetMaximumMessageSize() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getMaximumMessageSize()); + _serverConfig.initialise(); + assertEquals(0, _serverConfig.getMaximumMessageSize()); // Check value we set _config.setProperty("maximumMessageSize", 10L); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(10, serverConfig.getMaximumMessageSize()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(10, _serverConfig.getMaximumMessageSize()); } public void testGetMinimumAlertRepeatGap() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(0, serverConfig.getMinimumAlertRepeatGap()); + _serverConfig.initialise(); + assertEquals(0, _serverConfig.getMinimumAlertRepeatGap()); // Check value we set _config.setProperty("minimumAlertRepeatGap", 10L); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(10, serverConfig.getMinimumAlertRepeatGap()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(10, _serverConfig.getMinimumAlertRepeatGap()); } public void testGetProcessors() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(4, serverConfig.getProcessors()); + _serverConfig.initialise(); + assertEquals(4, _serverConfig.getConnectorProcessors()); // Check value we set _config.setProperty("connector.processors", 10); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(10, serverConfig.getProcessors()); - } + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(10, _serverConfig.getConnectorProcessors()); + } - public void testGetPort() throws ConfigurationException + public void testGetPorts() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertNotNull(serverConfig.getPorts()); - assertEquals(1, serverConfig.getPorts().size()); - assertEquals(5672, serverConfig.getPorts().get(0)); + _serverConfig.initialise(); + assertNotNull(_serverConfig.getPorts()); + assertEquals(1, _serverConfig.getPorts().size()); + assertEquals(5672, _serverConfig.getPorts().get(0)); // Check value we set _config.setProperty("connector.port", "10"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertNotNull(serverConfig.getPorts()); - assertEquals(1, serverConfig.getPorts().size()); - assertEquals("10", serverConfig.getPorts().get(0)); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertNotNull(_serverConfig.getPorts()); + assertEquals(1, _serverConfig.getPorts().size()); + assertEquals("10", _serverConfig.getPorts().get(0)); } public void testGetBind() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("wildcard", serverConfig.getBind()); + _serverConfig.initialise(); + assertEquals(WILDCARD_ADDRESS, _serverConfig.getBind()); // Check value we set _config.setProperty("connector.bind", "a"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("a", serverConfig.getBind()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("a", _serverConfig.getBind()); } public void testGetReceiveBufferSize() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(32767, serverConfig.getReceiveBufferSize()); + _serverConfig.initialise(); + assertEquals(ServerConfiguration.DEFAULT_BUFFER_SIZE, _serverConfig.getReceiveBufferSize()); // Check value we set _config.setProperty("connector.socketReceiveBuffer", "23"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(23, serverConfig.getReceiveBufferSize()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(23, _serverConfig.getReceiveBufferSize()); } public void testGetWriteBufferSize() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(32767, serverConfig.getWriteBufferSize()); + _serverConfig.initialise(); + assertEquals(ServerConfiguration.DEFAULT_BUFFER_SIZE, _serverConfig.getWriteBufferSize()); // Check value we set _config.setProperty("connector.socketWriteBuffer", "23"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(23, serverConfig.getWriteBufferSize()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(23, _serverConfig.getWriteBufferSize()); } public void testGetTcpNoDelay() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getTcpNoDelay()); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getTcpNoDelay()); // Check value we set _config.setProperty("connector.tcpNoDelay", false); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getTcpNoDelay()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getTcpNoDelay()); } public void testGetEnableExecutorPool() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getEnableExecutorPool()); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getEnableExecutorPool()); // Check value we set _config.setProperty("advanced.filterchain[@enableExecutorPool]", true); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getEnableExecutorPool()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getEnableExecutorPool()); } public void testGetEnableSSL() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getEnableSSL()); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getEnableSSL()); // Check value we set _config.setProperty("connector.ssl.enabled", true); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getEnableSSL()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getEnableSSL()); } public void testGetSSLOnly() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getSSLOnly()); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getSSLOnly()); // Check value we set _config.setProperty("connector.ssl.sslOnly", true); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getSSLOnly()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getSSLOnly()); } - public void testGetSSLPort() throws ConfigurationException + public void testGetSSLPorts() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(8672, serverConfig.getSSLPort()); + _serverConfig.initialise(); + assertNotNull(_serverConfig.getSSLPorts()); + assertEquals(1, _serverConfig.getSSLPorts().size()); + assertEquals(ServerConfiguration.DEFAULT_SSL_PORT, _serverConfig.getSSLPorts().get(0)); - // Check value we set - _config.setProperty("connector.ssl.port", 23); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(23, serverConfig.getSSLPort()); - } - - public void testGetKeystorePath() throws ConfigurationException - { - // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("none", serverConfig.getKeystorePath()); // Check value we set - _config.setProperty("connector.ssl.keystorePath", "a"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("a", serverConfig.getKeystorePath()); + _config.setProperty("connector.ssl.port", "10"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertNotNull(_serverConfig.getSSLPorts()); + assertEquals(1, _serverConfig.getSSLPorts().size()); + assertEquals("10", _serverConfig.getSSLPorts().get(0)); } - public void testGetKeystorePassword() throws ConfigurationException + public void testGetConnectorKeystorePath() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("none", serverConfig.getKeystorePassword()); + _serverConfig.initialise(); + assertNull(_serverConfig.getConnectorKeyStorePath()); // Check value we set - _config.setProperty("connector.ssl.keystorePassword", "a"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("a", serverConfig.getKeystorePassword()); + _config.setProperty("connector.ssl.keyStorePath", "a"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("a", _serverConfig.getConnectorKeyStorePath()); + + // Ensure we continue to support the old name keystorePath + _config.clearProperty("connector.ssl.keyStorePath"); + _config.setProperty("connector.ssl.keystorePath", "b"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("b", _serverConfig.getConnectorKeyStorePath()); } - public void testGetCertType() throws ConfigurationException + public void testGetConnectorKeystorePassword() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("SunX509", serverConfig.getCertType()); + _serverConfig.initialise(); + assertNull(_serverConfig.getConnectorKeyStorePassword()); // Check value we set - _config.setProperty("connector.ssl.certType", "a"); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals("a", serverConfig.getCertType()); + _config.setProperty("connector.ssl.keyStorePassword", "a"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("a", _serverConfig.getConnectorKeyStorePassword()); + + // Ensure we continue to support the old name keystorePassword + _config.clearProperty("connector.ssl.keyStorePassword"); + _config.setProperty("connector.ssl.keystorePassword", "b"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("b", _serverConfig.getConnectorKeyStorePassword()); } - public void testGetQpidNIO() throws ConfigurationException + public void testGetConnectorCertType() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getQpidNIO()); + _serverConfig.initialise(); + assertEquals("SunX509", _serverConfig.getConnectorCertType()); // Check value we set - _config.setProperty("connector.qpidnio", true); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getQpidNIO()); + _config.setProperty("connector.ssl.certType", "a"); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals("a", _serverConfig.getConnectorCertType()); } public void testGetUseBiasedWrites() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(false, serverConfig.getUseBiasedWrites()); + _serverConfig.initialise(); + assertEquals(false, _serverConfig.getUseBiasedWrites()); // Check value we set _config.setProperty("advanced.useWriteBiasedPool", true); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(true, serverConfig.getUseBiasedWrites()); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + assertEquals(true, _serverConfig.getUseBiasedWrites()); } - public void testGetHousekeepingExpiredMessageCheckPeriod() throws ConfigurationException + public void testGetHousekeepingCheckPeriod() throws ConfigurationException { // Check default - ServerConfiguration serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(30000, serverConfig.getHousekeepingCheckPeriod()); + _serverConfig.initialise(); + assertEquals(30000, _serverConfig.getHousekeepingCheckPeriod()); // Check value we set - _config.setProperty("housekeeping.expiredMessageCheckPeriod", 23L); - serverConfig = new ServerConfiguration(_config); - serverConfig.initialise(); - assertEquals(23, serverConfig.getHousekeepingCheckPeriod()); - serverConfig.setHousekeepingExpiredMessageCheckPeriod(42L); - assertEquals(42, serverConfig.getHousekeepingCheckPeriod()); + _config.setProperty("housekeeping.checkPeriod", 23L); + _serverConfig = new ServerConfiguration(_config); + _serverConfig.initialise(); + _serverConfig.setHousekeepingCheckPeriod(42L); + assertEquals(42, _serverConfig.getHousekeepingCheckPeriod()); } public void testSingleConfiguration() throws IOException, ConfigurationException @@ -767,7 +623,7 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase out.close(); ServerConfiguration conf = new ServerConfiguration(fileA); conf.initialise(); - assertEquals(4235, conf.getSSLPort()); + assertEquals("4235", conf.getSSLPorts().get(0)); } public void testCombinedConfiguration() throws IOException, ConfigurationException @@ -792,19 +648,17 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase out.close(); out = new FileWriter(fileB); - out.write("<broker><connector><ssl><port>2345</port></ssl><qpidnio>true</qpidnio></connector></broker>"); + out.write("<broker><connector><ssl><port>2345</port></ssl></connector></broker>"); out.close(); ServerConfiguration config = new ServerConfiguration(mainFile.getAbsoluteFile()); config.initialise(); - assertEquals(4235, config.getSSLPort()); // From first file, not + assertEquals("4235", config.getSSLPorts().get(0)); // From first file, not // overriden by second assertNotNull(config.getPorts()); assertEquals(1, config.getPorts().size()); assertEquals("2342", config.getPorts().get(0)); // From the first file, not // present in the second - assertEquals(true, config.getQpidNIO()); // From the second file, not - // present in the first } public void testVariableInterpolation() throws Exception @@ -835,9 +689,8 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase out.write("<broker>\n"); out.write("\t<management><enabled>false</enabled></management>\n"); out.write("\t<security>\n"); - out.write("\t\t<principal-databases>\n"); + out.write("\t\t<pd-auth-manager>\n"); out.write("\t\t\t<principal-database>\n"); - out.write("\t\t\t\t<name>passwordfile</name>\n"); out.write("\t\t\t\t<class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>\n"); out.write("\t\t\t\t<attributes>\n"); out.write("\t\t\t\t\t<attribute>\n"); @@ -846,11 +699,7 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase out.write("\t\t\t\t\t</attribute>\n"); out.write("\t\t\t\t</attributes>\n"); out.write("\t\t\t</principal-database>\n"); - out.write("\t\t</principal-databases>\n"); - out.write("\t\t<jmx>\n"); - out.write("\t\t\t<access>/dev/null</access>\n"); - out.write("\t\t\t<principal-database>passwordfile</principal-database>\n"); - out.write("\t\t</jmx>\n"); + out.write("\t\t</pd-auth-manager>\n"); out.write("\t\t<firewall>\n"); out.write("\t\t\t<rule access=\""+ ((allow) ? "allow" : "deny") +"\" network=\"127.0.0.1\"/>"); out.write("\t\t</firewall>\n"); @@ -886,9 +735,8 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase out.write("<broker>\n"); out.write("\t<management><enabled>false</enabled></management>\n"); out.write("\t<security>\n"); - out.write("\t\t<principal-databases>\n"); + out.write("\t\t<pd-auth-manager>\n"); out.write("\t\t\t<principal-database>\n"); - out.write("\t\t\t\t<name>passwordfile</name>\n"); out.write("\t\t\t\t<class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>\n"); out.write("\t\t\t\t<attributes>\n"); out.write("\t\t\t\t\t<attribute>\n"); @@ -897,11 +745,7 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase out.write("\t\t\t\t\t</attribute>\n"); out.write("\t\t\t\t</attributes>\n"); out.write("\t\t\t</principal-database>\n"); - out.write("\t\t</principal-databases>\n"); - out.write("\t\t<jmx>\n"); - out.write("\t\t\t<access>/dev/null</access>\n"); - out.write("\t\t\t<principal-database>passwordfile</principal-database>\n"); - out.write("\t\t</jmx>\n"); + out.write("\t\t</pd-auth-manager>\n"); out.write("\t\t<firewall>\n"); out.write("\t\t\t<rule access=\"allow\" network=\"127.0.0.1\"/>"); out.write("\t\t</firewall>\n"); @@ -992,9 +836,8 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase out.write("<broker>\n"); out.write("\t<management><enabled>false</enabled></management>\n"); out.write("\t<security>\n"); - out.write("\t\t<principal-databases>\n"); + out.write("\t\t<pd-auth-manager>\n"); out.write("\t\t\t<principal-database>\n"); - out.write("\t\t\t\t<name>passwordfile</name>\n"); out.write("\t\t\t\t<class>org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase</class>\n"); out.write("\t\t\t\t<attributes>\n"); out.write("\t\t\t\t\t<attribute>\n"); @@ -1003,11 +846,7 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase out.write("\t\t\t\t\t</attribute>\n"); out.write("\t\t\t\t</attributes>\n"); out.write("\t\t\t</principal-database>\n"); - out.write("\t\t</principal-databases>\n"); - out.write("\t\t<jmx>\n"); - out.write("\t\t\t<access>/dev/null</access>\n"); - out.write("\t\t\t<principal-database>passwordfile</principal-database>\n"); - out.write("\t\t</jmx>\n"); + out.write("\t\t</pd-auth-manager>\n"); out.write("\t\t<firewall>\n"); out.write("\t\t\t<rule access=\"allow\" network=\"127.0.0.1\"/>"); out.write("\t\t</firewall>\n"); @@ -1044,8 +883,9 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase writeConfigFile(mainFile, false, true, null, "test"); // Load config + ApplicationRegistry.remove(); ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); - ApplicationRegistry.initialise(reg, 1); + ApplicationRegistry.initialise(reg); // Test config VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); @@ -1076,8 +916,9 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase writeVirtualHostsFile(vhostsFile, "test"); // Load config + ApplicationRegistry.remove(); ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); - ApplicationRegistry.initialise(reg, 1); + ApplicationRegistry.initialise(reg); // Test config VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); @@ -1110,8 +951,9 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase writeConfigFile(mainFile, false, false, vhostsFile, null); // Load config + ApplicationRegistry.remove(); ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); - ApplicationRegistry.initialise(reg, 1); + ApplicationRegistry.initialise(reg); // Test config VirtualHostRegistry virtualHostRegistry = reg.getVirtualHostRegistry(); @@ -1153,9 +995,10 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase // Load config try - { + { + ApplicationRegistry.remove(); ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); - ApplicationRegistry.initialise(reg, 1); + ApplicationRegistry.initialise(reg); fail("Different virtualhost XML configurations not allowed"); } catch (ConfigurationException ce) @@ -1188,8 +1031,9 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase // Load config try { + ApplicationRegistry.remove(); ApplicationRegistry reg = new ConfigurationFileApplicationRegistry(mainFile); - ApplicationRegistry.initialise(reg, 1); + ApplicationRegistry.initialise(reg); fail("Multiple virtualhost XML configurations not allowed"); } catch (ConfigurationException ce) @@ -1481,4 +1325,106 @@ public class ServerConfigurationTest extends InternalBrokerBaseCase fail("Should throw a ConfigurationException"); } } + + /* + * Tests that the old element security.jmx.access (that used to be used + * to define JMX access rights) is rejected. + */ + public void testManagementAccessRejected() throws ConfigurationException + { + // Check default + _serverConfig.initialise(); + + // Check value we set + _config.setProperty("security.jmx.access(0)", "jmxremote.access"); + _serverConfig = new ServerConfiguration(_config); + + try + { + _serverConfig.initialise(); + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + assertEquals("Incorrect error message", + "Validation error : security/jmx/access is no longer a supported element within the configuration xml.", + ce.getMessage()); + } + } + + /* + * Tests that the old element security.jmx.principal-database (that used to define the + * principal database used for JMX authentication) is rejected. + */ + public void testManagementPrincipalDatabaseRejected() throws ConfigurationException + { + // Check default + _serverConfig.initialise(); + + // Check value we set + _config.setProperty("security.jmx.principal-database(0)", "mydb"); + _serverConfig = new ServerConfiguration(_config); + + try + { + _serverConfig.initialise(); + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + assertEquals("Incorrect error message", + "Validation error : security/jmx/principal-database is no longer a supported element within the configuration xml.", + ce.getMessage()); + } + } + + /* + * Tests that the old element security.principal-databases. ... (that used to define + * principal databases) is rejected. + */ + public void testPrincipalDatabasesRejected() throws ConfigurationException + { + _serverConfig.initialise(); + + // Check value we set + _config.setProperty("security.principal-databases.principal-database.class", "myclass"); + _serverConfig = new ServerConfiguration(_config); + + try + { + _serverConfig.initialise(); + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + assertEquals("Incorrect error message", + "Validation error : security/principal-databases is no longer supported within the configuration xml.", + ce.getMessage()); + } + } + + /* + * Tests that the old element housekeeping.expiredMessageCheckPeriod. ... (that was + * replaced by housekeeping.checkPeriod) is rejected. + */ + public void testExpiredMessageCheckPeriodRejected() throws ConfigurationException + { + _serverConfig.initialise(); + + // Check value we set + _config.setProperty("housekeeping.expiredMessageCheckPeriod", 23L); + _serverConfig = new ServerConfiguration(_config); + + try + { + _serverConfig.initialise(); + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + assertEquals("Incorrect error message", + "Validation error : housekeeping/expiredMessageCheckPeriod must be replaced by housekeeping/checkPeriod.", + ce.getMessage()); + } + } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java index 917755e8a5..b133d53ac5 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java @@ -20,6 +20,8 @@ package org.apache.qpid.server.configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.queue.AMQPriorityQueue; import org.apache.qpid.server.queue.AMQQueue; @@ -203,5 +205,50 @@ public class VirtualHostConfigurationTest extends InternalBrokerBaseCase } + /** + * Tests that the old element security.authentication.name is rejected. This element + * was never supported properly as authentication is performed before the virtual host + * is considered. + */ + public void testSecurityAuthenticationNameRejected() throws Exception + { + getConfigXml().addProperty("virtualhosts.virtualhost.testSecurityAuthenticationNameRejected.security.authentication.name", + "testdb"); + + try + { + super.createBroker(); + fail("Exception not thrown"); + } + catch(ConfigurationException ce) + { + assertEquals("Incorrect error message", + "Validation error : security/authentication/name is no longer a supported element within the configuration xml." + + " It appears in virtual host definition : " + getName(), + ce.getMessage()); + } + } + /* + * Tests that the old element housekeeping.expiredMessageCheckPeriod. ... (that was + * replaced by housekeeping.checkPeriod) is rejected. + */ + public void testExpiredMessageCheckPeriodRejected() throws Exception + { + getConfigXml().addProperty("virtualhosts.virtualhost.testExpiredMessageCheckPeriodRejected.housekeeping.expiredMessageCheckPeriod", + 5); + + try + { + super.createBroker(); + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + assertEquals("Incorrect error message", + "Validation error : housekeeping/expiredMessageCheckPeriod must be replaced by housekeeping/checkPeriod." + + " It appears in virtual host definition : " + getName(), + ce.getMessage()); + } + } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java index 7b58966a4c..3b7f5f3a51 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java @@ -52,6 +52,7 @@ import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.util.InternalBrokerBaseCase; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -276,7 +277,7 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase static ContentHeaderBody getContentHeader(FieldTable headers) { ContentHeaderBody header = new ContentHeaderBody(); - header.properties = getProperties(headers); + header.setProperties(getProperties(headers)); return header; } @@ -428,21 +429,11 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase //To change body of implemented methods use File | Settings | File Templates. } - public void reject(Subscription subscription) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isRejectedBy(Subscription subscription) + public boolean isRejectedBy(long subscriptionId) { return false; //To change body of implemented methods use File | Settings | File Templates. } - public void requeue(Subscription subscription) - { - //To change body of implemented methods use File | Settings | File Templates. - } - public void dequeue() { //To change body of implemented methods use File | Settings | File Templates. @@ -482,6 +473,16 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase { return 0; //To change body of implemented methods use File | Settings | File Templates. } + + public boolean isDequeued() + { + return false; + } + + public boolean isDispensed() + { + return false; + } }; if(action != null) @@ -565,8 +566,8 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase int pos = 0; for(ContentBody body : bodies) { - storedMessage.addContent(pos, body.payload.duplicate().buf()); - pos += body.payload.limit(); + storedMessage.addContent(pos, ByteBuffer.wrap(body._payload)); + pos += body._payload.length; } _incoming = new TestIncomingMessage(getMessageId(),publish, protocolsession); diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java index f72961c03c..403a290a0f 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java @@ -396,7 +396,7 @@ public class TopicExchangeTest extends InternalBrokerBaseCase IncomingMessage message = new IncomingMessage(info); final ContentHeaderBody chb = new ContentHeaderBody(); BasicContentHeaderProperties props = new BasicContentHeaderProperties(); - chb.properties = props; + chb.setProperties(props); message.setContentHeaderBody(chb); diff --git a/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLogger.java b/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLogger.java index 3752dcb37e..e8defd0e58 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLogger.java +++ b/java/broker/src/test/java/org/apache/qpid/server/logging/UnitTestMessageLogger.java @@ -28,11 +28,7 @@ import org.apache.qpid.server.logging.AbstractRootMessageLogger; public class UnitTestMessageLogger extends AbstractRootMessageLogger { - List<Object> _log; - - { - _log = new LinkedList<Object>(); - } + private final List<Object> _log = new LinkedList<Object>(); public UnitTestMessageLogger() { @@ -69,4 +65,14 @@ public class UnitTestMessageLogger extends AbstractRootMessageLogger { _log.clear(); } + + public boolean messageContains(final int index, final String contains) + { + if (index + 1 > _log.size()) + { + throw new IllegalArgumentException("Message with index " + index + " has not been logged"); + } + final String message = _log.get(index).toString(); + return message.contains(contains); + } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java b/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java index 033ae3b4b3..d6b790db01 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java @@ -20,13 +20,13 @@ */ package org.apache.qpid.server.logging.actors; -import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.logging.LogMessage; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.AMQException; - +import java.security.PrivilegedAction; +import java.util.Collections; import java.util.List; +import javax.management.remote.JMXPrincipal; +import javax.security.auth.Subject; + /** * Test : AMQPManagementActorTest * Validate the AMQPManagementActor class. @@ -96,8 +96,40 @@ public class ManagementActorTest extends BaseActorTestCase // Verify that the message has the right values assertTrue("Message contains the [mng: prefix", - logs.get(0).toString().contains("[mng:" + CONNECTION_ID + "(" + IP + ")")); + logs.get(0).toString().contains("[mng:N/A(" + IP + ")")); + } + + /** + * Tests appearance of principal name in log message + */ + public void testSubjectPrincipalNameAppearance() + { + Subject subject = new Subject(true, Collections.singleton(new JMXPrincipal("guest")), Collections.EMPTY_SET, + Collections.EMPTY_SET); + + final String message = Subject.doAs(subject, new PrivilegedAction<String>() + { + public String run() + { + return sendTestLogMessage(_amqpActor); + } + }); + + // Verify that the log message was created + assertNotNull("Test log message is not created!", message); + + List<Object> logs = _rawLogger.getLogMessages(); + + // Verify that at least one log message was added to log + assertEquals("Message log size not as expected.", 1, logs.size()); + + String logMessage = logs.get(0).toString(); + + // Verify that the logged message is present in the output + assertTrue("Message was not found in log message", logMessage.contains(message)); + // Verify that the message has the right principal value + assertTrue("Message contains the [mng: prefix", logMessage.contains("[mng:guest(" + IP + ")")); } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ExchangeMessagesTest.java b/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ExchangeMessagesTest.java index 728a98e009..4364376000 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ExchangeMessagesTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/logging/messages/ExchangeMessagesTest.java @@ -66,7 +66,6 @@ public class ExchangeMessagesTest extends AbstractTestMessages validateLogMessage(log, "EXH-1001", expected); } - public void testExchangeDeleted() { _logMessage = ExchangeMessages.DELETED(); @@ -77,4 +76,21 @@ public class ExchangeMessagesTest extends AbstractTestMessages validateLogMessage(log, "EXH-1002", expected); } + public void testExchangeDiscardedMessage() + { + // Get the Default Exchange on the Test Vhost for testing + final Exchange exchange = ApplicationRegistry.getInstance(). + getVirtualHostRegistry().getVirtualHost("test"). + getExchangeRegistry().getDefaultExchange(); + + final String name = exchange.getNameShortString().toString(); + final String routingKey = "routingKey"; + + _logMessage = ExchangeMessages.DISCARDMSG(name, routingKey); + List<Object> log = performLog(); + + String[] expected = {"Discarded Message :","Name:", name, "Routing Key:", routingKey}; + + validateLogMessage(log, "EXH-1003", expected); + } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java index a6c17e042e..f3ee2707b0 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/management/AMQUserManagementMBeanTest.java @@ -21,22 +21,26 @@ package org.apache.qpid.server.management; -import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import org.apache.commons.configuration.ConfigurationException; +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + + +import org.apache.commons.lang.NotImplementedException; +import org.apache.qpid.management.common.mbeans.UserManagement; import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean; import org.apache.qpid.server.util.InternalBrokerBaseCase; -/* Note: The main purpose is to test the jmx access rights file manipulation - * within AMQUserManagementMBean. The Principal Databases are tested by their own tests, - * this test just exercises their usage in AMQUserManagementMBean. +/** + * + * Tests the AMQUserManagementMBean and its interaction with the PrincipalDatabase. + * */ public class AMQUserManagementMBeanTest extends InternalBrokerBaseCase { @@ -44,7 +48,6 @@ public class AMQUserManagementMBeanTest extends InternalBrokerBaseCase private AMQUserManagementMBean _amqumMBean; private File _passwordFile; - private File _accessFile; private static final String TEST_USERNAME = "testuser"; private static final String TEST_PASSWORD = "password"; @@ -57,7 +60,6 @@ public class AMQUserManagementMBeanTest extends InternalBrokerBaseCase _database = new PlainPasswordFilePrincipalDatabase(); _amqumMBean = new AMQUserManagementMBean(); loadFreshTestPasswordFile(); - loadFreshTestAccessFile(); } @Override @@ -65,142 +67,67 @@ public class AMQUserManagementMBeanTest extends InternalBrokerBaseCase { //clean up test password/access files File _oldPasswordFile = new File(_passwordFile.getAbsolutePath() + ".old"); - File _oldAccessFile = new File(_accessFile.getAbsolutePath() + ".old"); _oldPasswordFile.delete(); - _oldAccessFile.delete(); _passwordFile.delete(); - _accessFile.delete(); super.tearDown(); } public void testDeleteUser() { - loadFreshTestPasswordFile(); - loadFreshTestAccessFile(); + assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); + assertTrue("Delete should return true to flag successful delete", _amqumMBean.deleteUser(TEST_USERNAME)); + assertEquals("Unexpected number of users after test", 0,_amqumMBean.viewUsers().size()); + } + + public void testDeleteUserWhereUserDoesNotExist() + { + assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); + assertFalse("Delete should return false to flag unsuccessful delete", _amqumMBean.deleteUser("made.up.username")); + assertEquals("Unexpected number of users after test", 1,_amqumMBean.viewUsers().size()); - //try deleting a non existant user - assertFalse(_amqumMBean.deleteUser("made.up.username")); - - assertTrue(_amqumMBean.deleteUser(TEST_USERNAME)); } - public void testDeleteUserIsSavedToAccessFile() + public void testCreateUser() { - loadFreshTestPasswordFile(); - loadFreshTestAccessFile(); - - assertTrue(_amqumMBean.deleteUser(TEST_USERNAME)); - - //check the access rights were actually deleted from the file - try{ - BufferedReader reader = new BufferedReader(new FileReader(_accessFile)); - - //check the 'generated by' comment line is present - assertTrue("File has no content", reader.ready()); - assertTrue("'Generated by' comment line was missing",reader.readLine().contains("Generated by " + - "AMQUserManagementMBean Console : Last edited by user:")); + assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); + assertTrue("Create should return true to flag successful create", _amqumMBean.createUser("newuser", "mypass")); + assertEquals("Unexpected number of users before test", 2,_amqumMBean.viewUsers().size()); + } - //there should also be a modified date/time comment line - assertTrue("File has no modified date/time comment line", reader.ready()); - assertTrue("Modification date/time comment line was missing",reader.readLine().startsWith("#")); - - //the access file should not contain any further data now as we just deleted the only user - assertFalse("User access data was present when it should have been deleted", reader.ready()); - } - catch (IOException e) - { - fail("Unable to valdate file contents due to:" + e.getMessage()); - } - + public void testCreateUserWhereUserAlreadyExists() + { + assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); + assertFalse("Create should return false to flag unsuccessful create", _amqumMBean.createUser(TEST_USERNAME, "mypass")); + assertEquals("Unexpected number of users before test", 1,_amqumMBean.viewUsers().size()); } - public void testSetRights() + public void testSetPassword() { - loadFreshTestPasswordFile(); - loadFreshTestAccessFile(); - - assertFalse(_amqumMBean.setRights("made.up.username", true, false, false)); - - assertTrue(_amqumMBean.setRights(TEST_USERNAME, true, false, false)); - assertTrue(_amqumMBean.setRights(TEST_USERNAME, false, true, false)); - assertTrue(_amqumMBean.setRights(TEST_USERNAME, false, false, true)); + assertTrue("Set password should return true to flag successful change", _amqumMBean.setPassword(TEST_USERNAME, "newpassword")); } - public void testSetRightsIsSavedToAccessFile() + public void testSetPasswordWhereUserDoesNotExist() { - loadFreshTestPasswordFile(); - loadFreshTestAccessFile(); - - assertTrue(_amqumMBean.setRights(TEST_USERNAME, false, false, true)); - - //check the access rights were actually updated in the file - try{ - BufferedReader reader = new BufferedReader(new FileReader(_accessFile)); - - //check the 'generated by' comment line is present - assertTrue("File has no content", reader.ready()); - assertTrue("'Generated by' comment line was missing",reader.readLine().contains("Generated by " + - "AMQUserManagementMBean Console : Last edited by user:")); - - //there should also be a modified date/time comment line - assertTrue("File has no modified date/time comment line", reader.ready()); - assertTrue("Modification date/time comment line was missing",reader.readLine().startsWith("#")); - - //the access file should not contain any further data now as we just deleted the only user - assertTrue("User access data was not updated in the access file", - reader.readLine().equals(TEST_USERNAME + "=" + MBeanInvocationHandlerImpl.ADMIN)); - - //the access file should not contain any further data now as we just deleted the only user - assertFalse("Additional user access data was present when there should be no more", reader.ready()); - } - catch (IOException e) - { - fail("Unable to valdate file contents due to:" + e.getMessage()); - } + assertFalse("Set password should return false to flag successful change", _amqumMBean.setPassword("made.up.username", "newpassword")); } - public void testSetAccessFileWithMissingFile() + public void testViewUsers() { - try - { - _amqumMBean.setAccessFile("made.up.filename"); - } - catch (IOException e) - { - fail("Should not have been an IOE." + e.getMessage()); - } - catch (ConfigurationException e) - { - assertTrue(e.getMessage(), e.getMessage().endsWith("does not exist")); - } - } - - public void testSetAccessFileWithReadOnlyFile() - { - File testFile = null; - try - { - testFile = File.createTempFile(this.getClass().getName(),".access.readonly"); - BufferedWriter passwordWriter = new BufferedWriter(new FileWriter(testFile, false)); - passwordWriter.write(TEST_USERNAME + ":" + TEST_PASSWORD); - passwordWriter.newLine(); - passwordWriter.flush(); - passwordWriter.close(); + TabularData userList = _amqumMBean.viewUsers(); - testFile.setReadOnly(); - _amqumMBean.setAccessFile(testFile.getPath()); - } - catch (IOException e) - { - fail("Access file was not created." + e.getMessage()); - } - catch (ConfigurationException e) - { - fail("There should not have been a configuration exception." + e.getMessage()); - } - - testFile.delete(); + assertNotNull(userList); + assertEquals("Unexpected number of users in user list", 1, userList.size()); + assertTrue(userList.containsKey(new Object[]{TEST_USERNAME})); + + // Check the deprecated read, write and admin items continue to exist but return false. + CompositeData userRec = userList.get(new Object[]{TEST_USERNAME}); + assertTrue(userRec.containsKey(UserManagement.RIGHTS_READ_ONLY)); + assertEquals(false, userRec.get(UserManagement.RIGHTS_READ_ONLY)); + assertEquals(false, userRec.get(UserManagement.RIGHTS_READ_WRITE)); + assertTrue(userRec.containsKey(UserManagement.RIGHTS_READ_WRITE)); + assertTrue(userRec.containsKey(UserManagement.RIGHTS_ADMIN)); + assertEquals(false, userRec.get(UserManagement.RIGHTS_ADMIN)); } // ============================ Utility methods ========================= @@ -227,37 +154,4 @@ public class AMQUserManagementMBeanTest extends InternalBrokerBaseCase fail("Unable to create test password file: " + e.getMessage()); } } - - private void loadFreshTestAccessFile() - { - try - { - if(_accessFile == null) - { - _accessFile = File.createTempFile(this.getClass().getName(),".access"); - } - - BufferedWriter accessWriter = new BufferedWriter(new FileWriter(_accessFile,false)); - accessWriter.write("#Last Updated By comment"); - accessWriter.newLine(); - accessWriter.write("#Date/time comment"); - accessWriter.newLine(); - accessWriter.write(TEST_USERNAME + "=" + MBeanInvocationHandlerImpl.READONLY); - accessWriter.newLine(); - accessWriter.flush(); - accessWriter.close(); - } - catch (IOException e) - { - fail("Unable to create test access file: " + e.getMessage()); - } - - try{ - _amqumMBean.setAccessFile(_accessFile.toString()); - } - catch (Exception e) - { - fail("Unable to set access file: " + e.getMessage()); - } - } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java b/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java deleted file mode 100644 index a64ec5d3b1..0000000000 --- a/java/broker/src/test/java/org/apache/qpid/server/plugins/MockPluginManager.java +++ /dev/null @@ -1,56 +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.plugins; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; -import org.apache.qpid.server.exchange.ExchangeType; -import org.apache.qpid.server.security.SecurityPluginFactory; - -public class MockPluginManager extends PluginManager -{ - private Map<String, SecurityPluginFactory> _securityPlugins = new HashMap<String, SecurityPluginFactory>(); - private Map<List<String>, ConfigurationPluginFactory> _configPlugins = new HashMap<List<String>, ConfigurationPluginFactory>(); - - public MockPluginManager(String pluginPath, String cachePath) throws Exception - { - super(pluginPath, cachePath); - } - - @Override - public Map<String, ExchangeType<?>> getExchanges() - { - return null; - } - - @Override - public Map<String, SecurityPluginFactory> getSecurityPlugins() - { - return _securityPlugins; - } - - @Override - public Map<List<String>, ConfigurationPluginFactory> getConfigurationPlugins() - { - return _configPlugins; - } -} diff --git a/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java b/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java new file mode 100644 index 0000000000..4a03445357 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java @@ -0,0 +1,93 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.plugins; + +import java.util.Map; +import java.util.TreeMap; + +import org.apache.qpid.test.utils.QpidTestCase; +import org.osgi.framework.Version; + +/** + * + */ +public class OsgiSystemPackageUtilTest extends QpidTestCase +{ + private OsgiSystemPackageUtil _util = null; // Object under test + + private Map<String, String> _map = new TreeMap<String, String>(); // Use a TreeMap for unit test in order for determinstic results. + + public void testWithOnePackage() throws Exception + { + _map.put("org.xyz", "1.0.0"); + + _util = new OsgiSystemPackageUtil(null, _map); + + final String systemPackageString = _util.getFormattedSystemPackageString(); + + assertEquals("org.xyz; version=1.0.0", systemPackageString); + } + + public void testWithTwoPackages() throws Exception + { + _map.put("org.xyz", "1.0.0"); + _map.put("org.abc", "1.2.3"); + + _util = new OsgiSystemPackageUtil(null, _map); + + final String systemPackageString = _util.getFormattedSystemPackageString(); + + assertEquals("org.abc; version=1.2.3, org.xyz; version=1.0.0", systemPackageString); + } + + public void testWithNoPackages() throws Exception + { + _util = new OsgiSystemPackageUtil(null, _map); + + final String systemPackageString = _util.getFormattedSystemPackageString(); + + assertNull(systemPackageString); + } + + public void testWithQpidPackageWithQpidReleaseNumberSet() throws Exception + { + _map.put("org.apache.qpid.xyz", "1.0.0"); + _map.put("org.abc", "1.2.3"); + + _util = new OsgiSystemPackageUtil(new Version("0.13"), _map); + + final String systemPackageString = _util.getFormattedSystemPackageString(); + + assertEquals("org.abc; version=1.2.3, org.apache.qpid.xyz; version=0.13.0", systemPackageString); + } + + public void testWithQpidPackageWithoutQpidReleaseNumberSet() throws Exception + { + _map.put("org.apache.qpid.xyz", "1.0.0"); + _map.put("org.abc", "1.2.3"); + + _util = new OsgiSystemPackageUtil(null, _map); + + final String systemPackageString = _util.getFormattedSystemPackageString(); + + assertEquals("org.abc; version=1.2.3, org.apache.qpid.xyz; version=1.0.0", systemPackageString); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java b/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java index 8c18ab85b0..8c945aabfb 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/plugins/PluginTest.java @@ -49,7 +49,7 @@ public class PluginTest extends InternalBrokerBaseCase public void testNoExchanges() throws Exception { - PluginManager manager = new PluginManager("/path/to/nowhere", "/tmp"); + PluginManager manager = new PluginManager("/path/to/nowhere", "/tmp", null); Map<String, ExchangeType<?>> exchanges = manager.getExchanges(); assertTrue("Exchanges found", exchanges.isEmpty()); } diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java index 3b6cd37ea9..5a411c6807 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java @@ -20,9 +20,16 @@ */ package org.apache.qpid.server.protocol; -import java.security.Principal; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import javax.security.auth.Subject; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; @@ -30,35 +37,31 @@ import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; -import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.message.AMQMessage; +import org.apache.qpid.server.message.MessageContentSource; +import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.state.AMQState; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.message.MessageContentSource; -import org.apache.qpid.transport.TestNetworkDriver; +import org.apache.qpid.transport.TestNetworkConnection; public class InternalTestProtocolSession extends AMQProtocolEngine implements ProtocolOutputConverter { // ChannelID(LIST) -> LinkedList<Pair> final Map<Integer, Map<AMQShortString, LinkedList<DeliveryPair>>> _channelDelivers; private AtomicInteger _deliveryCount = new AtomicInteger(0); + private static final AtomicLong ID_GENERATOR = new AtomicLong(0); public InternalTestProtocolSession(VirtualHost virtualHost) throws AMQException { - super(ApplicationRegistry.getInstance().getVirtualHostRegistry(), new TestNetworkDriver()); + super(ApplicationRegistry.getInstance().getVirtualHostRegistry(), new TestNetworkConnection(), ID_GENERATOR.getAndIncrement()); _channelDelivers = new HashMap<Integer, Map<AMQShortString, LinkedList<DeliveryPair>>>(); // Need to authenticate session for it to be representative testing. - setAuthorizedID(new Principal() - { - public String getName() - { - return "InternalTestProtocolSession"; - } - }); + setAuthorizedSubject(new Subject(true, Collections.singleton(new UsernamePrincipal("InternalTestProtocolSession")), + Collections.EMPTY_SET, Collections.EMPTY_SET)); setVirtualHost(virtualHost); } @@ -193,7 +196,7 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr return _closed; } - public void closeProtocolSession(boolean waitLast) + public void closeProtocolSession() { // Override as we don't have a real IOSession to close. // The alternative is to fully implement the TestIOSession to return a CloseFuture from close(); diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java new file mode 100644 index 0000000000..9d76d5efca --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java @@ -0,0 +1,146 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.nio.ByteBuffer; +import java.util.EnumSet; +import java.util.Set; + +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.protocol.ServerProtocolEngine; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.TestApplicationRegistry; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.transport.TestNetworkConnection; + +public class MultiVersionProtocolEngineFactoryTest extends QpidTestCase +{ + protected void setUp() throws Exception + { + super.setUp(); + + //the factory needs a registry instance + ApplicationRegistry.initialise(new TestApplicationRegistry(new ServerConfiguration(new XMLConfiguration()))); + } + + protected void tearDown() + { + //the factory opens a registry instance + ApplicationRegistry.remove(); + } + + private static final byte[] AMQP_0_8_HEADER = + new byte[] { (byte) 'A', + (byte) 'M', + (byte) 'Q', + (byte) 'P', + (byte) 1, + (byte) 1, + (byte) 8, + (byte) 0 + }; + + private static final byte[] AMQP_0_9_HEADER = + new byte[] { (byte) 'A', + (byte) 'M', + (byte) 'Q', + (byte) 'P', + (byte) 1, + (byte) 1, + (byte) 0, + (byte) 9 + }; + + private static final byte[] AMQP_0_9_1_HEADER = + new byte[] { (byte) 'A', + (byte) 'M', + (byte) 'Q', + (byte) 'P', + (byte) 0, + (byte) 0, + (byte) 9, + (byte) 1 + }; + + + private static final byte[] AMQP_0_10_HEADER = + new byte[] { (byte) 'A', + (byte) 'M', + (byte) 'Q', + (byte) 'P', + (byte) 1, + (byte) 1, + (byte) 0, + (byte) 10 + }; + + private byte[] getAmqpHeader(final AmqpProtocolVersion version) + { + switch(version) + { + case v0_8: + return AMQP_0_8_HEADER; + case v0_9: + return AMQP_0_9_HEADER; + case v0_9_1: + return AMQP_0_9_1_HEADER; + case v0_10: + return AMQP_0_10_HEADER; + default: + fail("unknown AMQP version, appropriate header must be added for new protocol version"); + return null; + } + } + + /** + * Test to verify that connections established using a MultiVersionProtocolEngine are assigned + * IDs from a common sequence, independent of the protocol version under use. + */ + public void testDifferentProtocolVersionsShareCommonIDNumberingSequence() + { + Set<AmqpProtocolVersion> versions = EnumSet.allOf(AmqpProtocolVersion.class); + + MultiVersionProtocolEngineFactory factory = + new MultiVersionProtocolEngineFactory("localhost", versions); + + //create a dummy to retrieve the 'current' ID number + long previousId = factory.newProtocolEngine(new TestNetworkConnection()).getConnectionId(); + + //create a protocol engine and send the AMQP header for all supported AMQP verisons, + //ensuring the ID assigned increases as expected + for(AmqpProtocolVersion version : versions) + { + long expectedID = previousId + 1; + byte[] header = getAmqpHeader(version); + assertNotNull("protocol header should not be null", header); + + ServerProtocolEngine engine = factory.newProtocolEngine(new TestNetworkConnection()); + assertEquals("ID did not increment as expected", expectedID, engine.getConnectionId()); + + //actually feed in the AMQP header for this protocol version, and ensure the ID remains consistent + engine.received(ByteBuffer.wrap(header)); + assertEquals("ID was not as expected following receipt of the AMQP version header", expectedID, engine.getConnectionId()); + + previousId = expectedID; + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java index d52f4c03f3..3961b3b355 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java @@ -96,7 +96,7 @@ public class AMQPriorityQueueTest extends SimpleAMQQueueTest AMQMessage msg = super.createMessage(id); BasicContentHeaderProperties props = new BasicContentHeaderProperties(); props.setPriority(i); - msg.getContentHeaderBody().properties = props; + msg.getContentHeaderBody().setProperties(props); return msg; } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java index 0707cab3d5..47b8b7eb18 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.queue; -import org.apache.mina.common.ByteBuffer; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentHeaderBody; @@ -277,7 +276,7 @@ public class AMQQueueAlertTest extends InternalBrokerBaseCase ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); BasicContentHeaderProperties props = new BasicContentHeaderProperties(); - contentHeaderBody.properties = props; + contentHeaderBody.setProperties(props); contentHeaderBody.bodySize = size; // in bytes IncomingMessage message = new IncomingMessage(publish); message.setContentHeaderBody(contentHeaderBody); @@ -289,7 +288,7 @@ public class AMQQueueAlertTest extends InternalBrokerBaseCase protected void configure() { // Increase Alert Check period - getConfiguration().setHousekeepingExpiredMessageCheckPeriod(200); + getConfiguration().setHousekeepingCheckPeriod(200); } private void sendMessages(AMQChannel channel, long messageCount, final long size) throws AMQException @@ -312,18 +311,14 @@ public class AMQQueueAlertTest extends InternalBrokerBaseCase { messages[i].addContentBodyFrame(new ContentChunk(){ - ByteBuffer _data = ByteBuffer.allocate((int)size); - - { - _data.limit((int)size); - } + byte[] _data = new byte[(int)size]; public int getSize() { return (int) size; } - public ByteBuffer getData() + public byte[] getData() { return _data; } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java index 5b72cfac40..070d105805 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java @@ -37,7 +37,6 @@ import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.protocol.InternalTestProtocolSession; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.TestableMemoryMessageStore; -import org.apache.mina.common.ByteBuffer; import javax.management.JMException; @@ -275,18 +274,14 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase msg.addContentBodyFrame(new ContentChunk() { - ByteBuffer _data = ByteBuffer.allocate((int)MESSAGE_SIZE); - - { - _data.limit((int)MESSAGE_SIZE); - } + byte[] _data = new byte[((int)MESSAGE_SIZE)]; public int getSize() { return (int) MESSAGE_SIZE; } - public ByteBuffer getData() + public byte[] getData() { return _data; } @@ -402,8 +397,8 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); contentHeaderBody.bodySize = MESSAGE_SIZE; // in bytes - contentHeaderBody.properties = new BasicContentHeaderProperties(); - ((BasicContentHeaderProperties) contentHeaderBody.properties).setDeliveryMode((byte) (persistent ? 2 : 1)); + contentHeaderBody.setProperties(new BasicContentHeaderProperties()); + ((BasicContentHeaderProperties) contentHeaderBody.getProperties()).setDeliveryMode((byte) (persistent ? 2 : 1)); IncomingMessage msg = new IncomingMessage(publish); msg.setContentHeaderBody(contentHeaderBody); return msg; @@ -441,8 +436,7 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase getSession().getMethodRegistry() .getProtocolVersionMethodConverter() .convertToContentChunk( - new ContentBody(ByteBuffer.allocate((int) MESSAGE_SIZE), - MESSAGE_SIZE))); + new ContentBody(new byte[(int) MESSAGE_SIZE]))); AMQMessage m = new AMQMessage(currentMessage.getStoredMessage()); for(BaseQueue q : currentMessage.getDestinationQueues()) diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java index 04608275a3..0f5374b3e5 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java @@ -126,7 +126,7 @@ public class AckTest extends InternalBrokerBaseCase //IncomingMessage msg2 = null; BasicContentHeaderProperties b = new BasicContentHeaderProperties(); ContentHeaderBody cb = new ContentHeaderBody(); - cb.properties = b; + cb.setProperties(b); if (persistent) { diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java index 888a16053c..4c31092983 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -29,7 +29,7 @@ import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.security.PrincipalHolder; +import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.binding.Binding; @@ -48,7 +48,7 @@ public class MockAMQQueue implements AMQQueue private AMQShortString _name; private VirtualHost _virtualhost; - private PrincipalHolder _principalHolder; + private AuthorizationHolder _authorizationHolder; private AMQSessionModel _exclusiveOwner; private AMQShortString _owner; @@ -536,14 +536,14 @@ public class MockAMQQueue implements AMQQueue return null; //To change body of implemented methods use File | Settings | File Templates. } - public PrincipalHolder getPrincipalHolder() + public AuthorizationHolder getAuthorizationHolder() { - return _principalHolder; + return _authorizationHolder; } - public void setPrincipalHolder(PrincipalHolder principalHolder) + public void setAuthorizationHolder(final AuthorizationHolder authorizationHolder) { - _principalHolder = principalHolder; + _authorizationHolder = authorizationHolder; } public AMQSessionModel getExclusiveOwningSession() diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java index 5bdbe2c68e..ab8850c18c 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java @@ -139,7 +139,7 @@ public class MockQueueEntry implements QueueEntry } - public boolean isRejectedBy(Subscription subscription) + public boolean isRejectedBy(long subscriptionId) { return false; @@ -153,13 +153,6 @@ public class MockQueueEntry implements QueueEntry } - public void reject(Subscription subscription) - { - - - } - - public void release() { @@ -231,4 +224,14 @@ public class MockQueueEntry implements QueueEntry _message = msg; } + public boolean isDequeued() + { + return false; + } + + public boolean isDispensed() + { + return false; + } + } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTest.java new file mode 100644 index 0000000000..d8afd8d829 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTest.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 java.lang.reflect.Field; + +import junit.framework.TestCase; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.AMQMessage; +import org.apache.qpid.server.queue.QueueEntry.EntryState; +import org.apache.qpid.server.subscription.MockSubscription; +import org.apache.qpid.server.subscription.Subscription; + +/** + * Tests for {@link QueueEntryImpl} + * + */ +public class QueueEntryImplTest extends TestCase +{ + // tested entry + private QueueEntryImpl _queueEntry; + + public void setUp() throws Exception + { + AMQMessage message = new MockAMQMessage(1); + SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(new MockAMQQueue("test")); + _queueEntry = new QueueEntryImpl(queueEntryList, message, 1); + } + + public void testAquire() + { + assertTrue("Queue entry should be in AVAILABLE state before invoking of acquire method", + _queueEntry.isAvailable()); + acquire(); + } + + public void testDequeue() + { + dequeue(); + } + + public void testDelete() + { + delete(); + } + + /** + * Tests release method for entry in acquired state. + * <p> + * Entry in state ACQUIRED should be released and its status should be + * changed to AVAILABLE. + */ + public void testReleaseAquired() + { + acquire(); + _queueEntry.release(); + assertTrue("Queue entry should be in AVAILABLE state after invoking of release method", + _queueEntry.isAvailable()); + } + + /** + * Tests release method for entry in dequeued state. + * <p> + * Invoking release on dequeued entry should not have any effect on its + * state. + */ + public void testReleaseDequeued() + { + dequeue(); + _queueEntry.release(); + EntryState state = getState(); + assertEquals("Invoking of release on entry in DEQUEUED state should not have any effect", + QueueEntry.DEQUEUED_STATE, state); + } + + /** + * Tests release method for entry in deleted state. + * <p> + * Invoking release on deleted entry should not have any effect on its + * state. + */ + public void testReleaseDeleted() + { + delete(); + _queueEntry.release(); + assertTrue("Invoking of release on entry in DELETED state should not have any effect", + _queueEntry.isDeleted()); + } + + /** + * Tests if entries in DEQUQUED or DELETED state are not returned by getNext method. + */ + public void testGetNext() + { + int numberOfEntries = 5; + QueueEntryImpl[] entries = new QueueEntryImpl[numberOfEntries]; + SimpleQueueEntryList queueEntryList = new SimpleQueueEntryList(new MockAMQQueue("test")); + + // create test entries + for(int i = 0; i < numberOfEntries ; i++) + { + AMQMessage message = null;; + try + { + message = new MockAMQMessage(i); + } + catch (AMQException e) + { + fail("Failure to create a mock message:" + e.getMessage()); + } + QueueEntryImpl entry = (QueueEntryImpl)queueEntryList.add(message); + entries[i] = entry; + } + + // test getNext for not acquired entries + for(int i = 0; i < numberOfEntries ; i++) + { + QueueEntryImpl queueEntry = entries[i]; + QueueEntryImpl next = queueEntry.getNext(); + if (i < numberOfEntries - 1) + { + assertEquals("Unexpected entry from QueueEntryImpl#getNext()", entries[i + 1], next); + } + else + { + assertNull("The next entry after the last should be null", next); + } + } + + // delete second + entries[1].acquire(); + entries[1].delete(); + + // dequeue third + entries[2].acquire(); + entries[2].dequeue(); + + QueueEntryImpl next = entries[0].getNext(); + assertEquals("expected forth entry",entries[3], next); + next = next.getNext(); + assertEquals("expected fifth entry", entries[4], next); + next = next.getNext(); + assertNull("The next entry after the last should be null", next); + } + /** + * A helper method to put tested object into deleted state and assert the state + */ + private void delete() + { + _queueEntry.delete(); + assertTrue("Queue entry should be in DELETED state after invoking of delete method", + _queueEntry.isDeleted()); + } + + /** + * A helper method to put tested entry into dequeue state and assert the sate + */ + private void dequeue() + { + acquire(); + _queueEntry.dequeue(); + EntryState state = getState(); + assertEquals("Queue entry should be in DEQUEUED state after invoking of dequeue method", + QueueEntry.DEQUEUED_STATE, state); + } + + /** + * A helper method to put tested entry into acquired state and assert the sate + */ + private void acquire() + { + _queueEntry.acquire(new MockSubscription()); + assertTrue("Queue entry should be in ACQUIRED state after invoking of acquire method", + _queueEntry.isAcquired()); + } + + /** + * A helper method to get entry state + * + * @return entry state + */ + private EntryState getState() + { + EntryState state = null; + try + { + Field f = QueueEntryImpl.class.getDeclaredField("_state"); + f.setAccessible(true); + state = (EntryState) f.get(_queueEntry); + } + catch (Exception e) + { + fail("Failure to get a state field: " + e.getMessage()); + } + return state; + } + + /** + * Tests rejecting a queue entry records the Subscription ID + * for later verification by isRejectedBy(subscriptionId). + */ + public void testRejectAndRejectedBy() + { + Subscription sub = new MockSubscription(); + long subId = sub.getSubscriptionID(); + + assertFalse("Queue entry should not yet have been rejected by the subscription", _queueEntry.isRejectedBy(subId)); + assertFalse("Queue entry should not yet have been acquired by a subscription", _queueEntry.isAcquired()); + + //acquire, reject, and release the message using the subscription + assertTrue("Queue entry should have been able to be acquired", _queueEntry.acquire(sub)); + _queueEntry.reject(); + _queueEntry.release(); + + //verify the rejection is recorded + assertTrue("Queue entry should have been rejected by the subscription", _queueEntry.isRejectedBy(subId)); + + //repeat rejection using a second subscription + Subscription sub2 = new MockSubscription(); + long sub2Id = sub2.getSubscriptionID(); + + assertFalse("Queue entry should not yet have been rejected by the subscription", _queueEntry.isRejectedBy(sub2Id)); + assertTrue("Queue entry should have been able to be acquired", _queueEntry.acquire(sub2)); + _queueEntry.reject(); + + //verify it still records being rejected by both subscriptions + assertTrue("Queue entry should have been rejected by the subscription", _queueEntry.isRejectedBy(subId)); + assertTrue("Queue entry should have been rejected by the subscription", _queueEntry.isRejectedBy(sub2Id)); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java index 67d093d00a..f4cdbbe02c 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java @@ -36,13 +36,16 @@ import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.exchange.DirectExchange; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.MessageMetaData; +import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.BaseQueue.PostEnqueueAction; +import org.apache.qpid.server.queue.SimpleAMQQueue.QueueEntryFilter; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.subscription.MockSubscription; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.util.InternalBrokerBaseCase; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -51,6 +54,8 @@ import org.apache.qpid.server.virtualhost.VirtualHostImpl; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; public class SimpleAMQQueueTest extends InternalBrokerBaseCase { @@ -102,7 +107,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase ApplicationRegistry applicationRegistry = (ApplicationRegistry)ApplicationRegistry.getInstance(); PropertiesConfiguration env = new PropertiesConfiguration(); - _virtualHost = new VirtualHostImpl(new VirtualHostConfiguration(getClass().getName(), env), _store); + _virtualHost = new VirtualHostImpl(ApplicationRegistry.getInstance(), new VirtualHostConfiguration(getClass().getName(), env), _store); applicationRegistry.getVirtualHostRegistry().registerVirtualHost(_virtualHost); _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(_qname, false, _owner, false, false, _virtualHost, _arguments); @@ -227,10 +232,10 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase } /** - * Tests that a re-queued message is resent to the subscriber. Verifies also that the + * Tests that a released queue entry is resent to the subscriber. Verifies also that the * QueueContext._releasedEntry is reset to null after the entry has been reset. */ - public void testRequeuedMessageIsResentToSubscriber() throws Exception + public void testReleasedMessageIsResentToSubscriber() throws Exception { _queue.registerSubscription(_subscription, false); @@ -253,19 +258,18 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase _queue.enqueue(messageB, postEnqueueAction); _queue.enqueue(messageC, postEnqueueAction); - Thread.sleep(150); // Work done by SubFlushRunner Thread + Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads assertEquals("Unexpected total number of messages sent to subscription", 3, _subscription.getMessages().size()); assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered()); assertFalse("Redelivery flag should not be set", queueEntries.get(1).isRedelivered()); assertFalse("Redelivery flag should not be set", queueEntries.get(2).isRedelivered()); - /* Now requeue the first message only */ + /* Now release the first message only, causing it to be requeued */ queueEntries.get(0).release(); - _queue.requeue(queueEntries.get(0)); - Thread.sleep(150); // Work done by SubFlushRunner Thread + Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads assertEquals("Unexpected total number of messages sent to subscription", 4, _subscription.getMessages().size()); assertTrue("Redelivery flag should now be set", queueEntries.get(0).isRedelivered()); @@ -275,11 +279,11 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase } /** - * Tests that a re-queued message that becomes expired is not resent to the subscriber. + * Tests that a released message that becomes expired is not resent to the subscriber. * This tests ensures that SimpleAMQQueueEntry.getNextAvailableEntry avoids expired entries. * Verifies also that the QueueContext._releasedEntry is reset to null after the entry has been reset. */ - public void testRequeuedMessageThatBecomesExpiredIsNotRedelivered() throws Exception + public void testReleaseMessageThatBecomesExpiredIsNotRedelivered() throws Exception { _queue.registerSubscription(_subscription, false); @@ -301,17 +305,16 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase _queue.enqueue(messageA, postEnqueueAction); int subFlushWaitTime = 150; - Thread.sleep(subFlushWaitTime); // Work done by SubFlushRunner Thread + Thread.sleep(subFlushWaitTime); // Work done by SubFlushRunner/QueueRunner Threads assertEquals("Unexpected total number of messages sent to subscription", 1, _subscription.getMessages().size()); assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered()); - /* Wait a little more to be sure that message will have expired, then requeue it */ + /* Wait a little more to be sure that message will have expired, then release the first message only, causing it to be requeued */ Thread.sleep(messageExpirationOffset - subFlushWaitTime + 10); queueEntries.get(0).release(); - _queue.requeue(queueEntries.get(0)); - Thread.sleep(subFlushWaitTime); // Work done by SubFlushRunner Thread + Thread.sleep(subFlushWaitTime); // Work done by SubFlushRunner/QueueRunner Threads assertTrue("Expecting the queue entry to be now expired", queueEntries.get(0).expired()); assertEquals("Total number of messages sent should not have changed", 1, _subscription.getMessages().size()); @@ -321,12 +324,12 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase } /** - * Tests that if a client requeues messages 'out of order' (the order + * Tests that if a client releases entries 'out of order' (the order * used by QueueEntryImpl.compareTo) that messages are still resent * successfully. Specifically this test ensures the {@see SimpleAMQQueue#requeue()} * can correctly move the _releasedEntry to an earlier position in the QueueEntry list. */ - public void testMessagesRequeuedOutOfComparableOrderAreDelivered() throws Exception + public void testReleasedOutOfComparableOrderAreRedelivered() throws Exception { _queue.registerSubscription(_subscription, false); @@ -349,21 +352,19 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase _queue.enqueue(messageB, postEnqueueAction); _queue.enqueue(messageC, postEnqueueAction); - Thread.sleep(150); // Work done by SubFlushRunner Thread + Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads assertEquals("Unexpected total number of messages sent to subscription", 3, _subscription.getMessages().size()); assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered()); assertFalse("Redelivery flag should not be set", queueEntries.get(1).isRedelivered()); assertFalse("Redelivery flag should not be set", queueEntries.get(2).isRedelivered()); - /* Now requeue the third and first message only */ + /* Now release the third and first message only, causing it to be requeued */ queueEntries.get(2).release(); queueEntries.get(0).release(); - _queue.requeue(queueEntries.get(2)); - _queue.requeue(queueEntries.get(0)); - Thread.sleep(150); // Work done by SubFlushRunner Thread + Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads assertEquals("Unexpected total number of messages sent to subscription", 5, _subscription.getMessages().size()); assertTrue("Redelivery flag should now be set", queueEntries.get(0).isRedelivered()); @@ -374,10 +375,10 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** - * Tests a requeue for a queue with multiple subscriptions. Verifies that a + * Tests that a release requeues an entry for a queue with multiple subscriptions. Verifies that a * requeue resends a message to a <i>single</i> subscriber. */ - public void testRequeueForQueueWithMultipleSubscriptions() throws Exception + public void testReleaseForQueueWithMultipleSubscriptions() throws Exception { MockSubscription subscription1 = new MockSubscription(); MockSubscription subscription2 = new MockSubscription(); @@ -402,66 +403,16 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase _queue.enqueue(messageA, postEnqueueAction); _queue.enqueue(messageB, postEnqueueAction); - Thread.sleep(150); // Work done by SubFlushRunner Thread + Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to subscription1 after enqueue", 1, subscription1.getMessages().size()); - assertEquals("Unexpected total number of messages sent to subscription2 after enqueue", 1, subscription2.getMessages().size()); + assertEquals("Unexpected total number of messages sent to both after enqueue", 2, subscription1.getMessages().size() + subscription2.getMessages().size()); - /* Now requeue a message (for any subscription) */ + /* Now release the first message only, causing it to be requeued */ + queueEntries.get(0).release(); - queueEntries.get(0).release(); - _queue.requeue((QueueEntryImpl)queueEntries.get(0)); - - Thread.sleep(150); // Work done by SubFlushRunner Thread + Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to all subscriptions after requeue", 3, subscription1.getMessages().size() + subscription2.getMessages().size()); - assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)subscription1.getQueueContext())._releasedEntry); - assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)subscription2.getQueueContext())._releasedEntry); - } - - /** - * Tests a requeue for a queue with multiple subscriptions. Verifies that a - * subscriber specific requeue resends the message to <i>that</i> subscriber. - */ - public void testSubscriptionSpecificRequeueForQueueWithMultipleSubscriptions() throws Exception - { - MockSubscription subscription1 = new MockSubscription(); - MockSubscription subscription2 = new MockSubscription(); - - _queue.registerSubscription(subscription1, false); - _queue.registerSubscription(subscription2, false); - - final ArrayList<QueueEntry> queueEntries = new ArrayList<QueueEntry>(); - PostEnqueueAction postEnqueueAction = new PostEnqueueAction() - { - public void onEnqueue(QueueEntry entry) - { - queueEntries.add(entry); - } - }; - - AMQMessage messageA = createMessage(new Long(24)); - AMQMessage messageB = createMessage(new Long(25)); - - /* Enqueue two messages */ - - _queue.enqueue(messageA, postEnqueueAction); - _queue.enqueue(messageB, postEnqueueAction); - - Thread.sleep(150); // Work done by SubFlushRunner Thread - - assertEquals("Unexpected total number of messages sent to subscription1 after enqueue", 1, subscription1.getMessages().size()); - assertEquals("Unexpected total number of messages sent to subscription2 after enqueue", 1, subscription2.getMessages().size()); - - /* Now requeue a message (for first subscription) */ - - queueEntries.get(0).release(); - _queue.requeue((QueueEntryImpl)queueEntries.get(0), subscription1); - - Thread.sleep(150); // Work done by SubFlushRunner Thread - - assertEquals("Unexpected total number of messages sent to subscription1 after requeue", 2, subscription1.getMessages().size()); - assertEquals("Unexpected total number of messages sent to subscription2 after requeue", 1, subscription2.getMessages().size()); + assertEquals("Unexpected total number of messages sent to both subscriptions after release", 3, subscription1.getMessages().size() + subscription2.getMessages().size()); assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)subscription1.getQueueContext())._releasedEntry); assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)subscription2.getQueueContext())._releasedEntry); } @@ -660,8 +611,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // Create IncomingMessage and nondurable queue final IncomingMessage msg = new IncomingMessage(info); ContentHeaderBody contentHeaderBody = new ContentHeaderBody(); - contentHeaderBody.properties = new BasicContentHeaderProperties(); - ((BasicContentHeaderProperties) contentHeaderBody.properties).setDeliveryMode((byte) 2); + contentHeaderBody.setProperties(new BasicContentHeaderProperties()); + ((BasicContentHeaderProperties) contentHeaderBody.getProperties()).setDeliveryMode((byte) 2); msg.setContentHeaderBody(contentHeaderBody); final ArrayList<BaseQueue> qs = new ArrayList<BaseQueue>(); @@ -707,6 +658,635 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase } + /** + * processQueue() is used when asynchronously delivering messages to + * subscriptions which could not be delivered immediately during the + * enqueue() operation. + * + * A defect within the method would mean that delivery of these messages may + * not occur should the Runner stop before all messages have been processed. + * Such a defect was discovered when Selectors were used such that one and + * only one subscription can/will accept any given messages, but multiple + * subscriptions are present, and one of the earlier subscriptions receives + * more messages than the others. + * + * This test is to validate that the processQueue() method is able to + * correctly deliver all of the messages present for asynchronous delivery + * to subscriptions in such a scenario. + */ + public void testProcessQueueWithUniqueSelectors() throws Exception + { + TestSimpleQueueEntryListFactory factory = new TestSimpleQueueEntryListFactory(); + SimpleAMQQueue testQueue = new SimpleAMQQueue("testQueue", false, "testOwner",false, + false, _virtualHost, factory, null) + { + @Override + public void deliverAsync(Subscription sub) + { + // do nothing, i.e prevent deliveries by the SubFlushRunner + // when registering the new subscriptions + } + }; + + // retrieve the QueueEntryList the queue creates and insert the test + // messages, thus avoiding straight-through delivery attempts during + //enqueue() process. + QueueEntryList list = factory.getQueueEntryList(); + assertNotNull("QueueEntryList should have been created", list); + + QueueEntry msg1 = list.add(createMessage(1L)); + QueueEntry msg2 = list.add(createMessage(2L)); + QueueEntry msg3 = list.add(createMessage(3L)); + QueueEntry msg4 = list.add(createMessage(4L)); + QueueEntry msg5 = list.add(createMessage(5L)); + + // Create lists of the entries each subscription should be interested + // in.Bias over 50% of the messages to the first subscription so that + // the later subscriptions reject them and report being done before + // the first subscription as the processQueue method proceeds. + List<QueueEntry> msgListSub1 = createEntriesList(msg1, msg2, msg3); + List<QueueEntry> msgListSub2 = createEntriesList(msg4); + List<QueueEntry> msgListSub3 = createEntriesList(msg5); + + MockSubscription sub1 = new MockSubscription(msgListSub1); + MockSubscription sub2 = new MockSubscription(msgListSub2); + MockSubscription sub3 = new MockSubscription(msgListSub3); + + // register the subscriptions + testQueue.registerSubscription(sub1, false); + testQueue.registerSubscription(sub2, false); + testQueue.registerSubscription(sub3, false); + + //check that no messages have been delivered to the + //subscriptions during registration + assertEquals("No messages should have been delivered yet", 0, sub1.getMessages().size()); + assertEquals("No messages should have been delivered yet", 0, sub2.getMessages().size()); + assertEquals("No messages should have been delivered yet", 0, sub3.getMessages().size()); + + // call processQueue to deliver the messages + testQueue.processQueue(new QueueRunner(testQueue, 1) + { + @Override + public void run() + { + // we dont actually want/need this runner to do any work + // because we we are already doing it! + } + }); + + // check expected messages delivered to correct consumers + verifyRecievedMessages(msgListSub1, sub1.getMessages()); + verifyRecievedMessages(msgListSub2, sub2.getMessages()); + verifyRecievedMessages(msgListSub3, sub3.getMessages()); + } + + /** + * Tests that dequeued message is not present in the list returned form + * {@link SimpleAMQQueue#getMessagesOnTheQueue()} + */ + public void testGetMessagesOnTheQueueWithDequeuedEntry() + { + int messageNumber = 4; + int dequeueMessageIndex = 1; + + // send test messages into a test queue + enqueueGivenNumberOfMessages(_queue, messageNumber); + + // dequeue message + dequeueMessage(_queue, dequeueMessageIndex); + + // get messages on the queue + List<QueueEntry> entries = _queue.getMessagesOnTheQueue(); + + // assert queue entries + assertEquals(messageNumber - 1, entries.size()); + int expectedId = 0; + for (int i = 0; i < messageNumber - 1; i++) + { + Long id = ((AMQMessage) entries.get(i).getMessage()).getMessageId(); + if (i == dequeueMessageIndex) + { + assertFalse("Message with id " + dequeueMessageIndex + + " was dequeued and should not be returned by method getMessagesOnTheQueue!", + new Long(expectedId).equals(id)); + expectedId++; + } + assertEquals("Expected message with id " + expectedId + " but got message with id " + id, + new Long(expectedId), id); + expectedId++; + } + } + + /** + * Tests that dequeued message is not present in the list returned form + * {@link SimpleAMQQueue#getMessagesOnTheQueue(QueueEntryFilter)} + */ + public void testGetMessagesOnTheQueueByQueueEntryFilterWithDequeuedEntry() + { + int messageNumber = 4; + int dequeueMessageIndex = 1; + + // send test messages into a test queue + enqueueGivenNumberOfMessages(_queue, messageNumber); + + // dequeue message + dequeueMessage(_queue, dequeueMessageIndex); + + // get messages on the queue with filter accepting all available messages + List<QueueEntry> entries = _queue.getMessagesOnTheQueue(new QueueEntryFilter() + { + public boolean accept(QueueEntry entry) + { + return true; + } + + public boolean filterComplete() + { + return false; + } + }); + + // assert entries on the queue + assertEquals(messageNumber - 1, entries.size()); + int expectedId = 0; + for (int i = 0; i < messageNumber - 1; i++) + { + Long id = ((AMQMessage) entries.get(i).getMessage()).getMessageId(); + if (i == dequeueMessageIndex) + { + assertFalse("Message with id " + dequeueMessageIndex + + " was dequeued and should not be returned by method getMessagesOnTheQueue!", + new Long(expectedId).equals(id)); + expectedId++; + } + assertEquals("Expected message with id " + expectedId + " but got message with id " + id, + new Long(expectedId), id); + expectedId++; + } + } + + /** + * Tests that dequeued message is not copied as part of invocation of + * {@link SimpleAMQQueue#copyMessagesToAnotherQueue(long, long, String, StoreContext)} + */ + public void testCopyMessagesWithDequeuedEntry() + { + int messageNumber = 4; + int dequeueMessageIndex = 1; + String anotherQueueName = "testQueue2"; + + // put test messages into a test queue + enqueueGivenNumberOfMessages(_queue, messageNumber); + + // dequeue message + dequeueMessage(_queue, dequeueMessageIndex); + + // create another queue + SimpleAMQQueue queue = createQueue(anotherQueueName); + + // create transaction + ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getTransactionLog()); + + // copy messages into another queue + _queue.copyMessagesToAnotherQueue(0, messageNumber, anotherQueueName, txn); + + // commit transaction + txn.commit(); + + // get messages on another queue + List<QueueEntry> entries = queue.getMessagesOnTheQueue(); + + // assert another queue entries + assertEquals(messageNumber - 1, entries.size()); + int expectedId = 0; + for (int i = 0; i < messageNumber - 1; i++) + { + Long id = ((AMQMessage)entries.get(i).getMessage()).getMessageId(); + if (i == dequeueMessageIndex) + { + assertFalse("Message with id " + dequeueMessageIndex + + " was dequeued and should not been copied into another queue!", + new Long(expectedId).equals(id)); + expectedId++; + } + assertEquals("Expected message with id " + expectedId + " but got message with id " + id, + new Long(expectedId), id); + expectedId++; + } + } + + /** + * Tests that dequeued message is not moved as part of invocation of + * {@link SimpleAMQQueue#moveMessagesToAnotherQueue(long, long, String, StoreContext)} + */ + public void testMovedMessagesWithDequeuedEntry() + { + int messageNumber = 4; + int dequeueMessageIndex = 1; + String anotherQueueName = "testQueue2"; + + // put messages into a test queue + enqueueGivenNumberOfMessages(_queue, messageNumber); + + // dequeue message + dequeueMessage(_queue, dequeueMessageIndex); + + // create another queue + SimpleAMQQueue queue = createQueue(anotherQueueName); + + // create transaction + ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getTransactionLog()); + + // move messages into another queue + _queue.moveMessagesToAnotherQueue(0, messageNumber, anotherQueueName, txn); + + // commit transaction + txn.commit(); + + // get messages on another queue + List<QueueEntry> entries = queue.getMessagesOnTheQueue(); + + // assert another queue entries + assertEquals(messageNumber - 1, entries.size()); + int expectedId = 0; + for (int i = 0; i < messageNumber - 1; i++) + { + Long id = ((AMQMessage)entries.get(i).getMessage()).getMessageId(); + if (i == dequeueMessageIndex) + { + assertFalse("Message with id " + dequeueMessageIndex + + " was dequeued and should not been copied into another queue!", + new Long(expectedId).equals(id)); + expectedId++; + } + assertEquals("Expected message with id " + expectedId + " but got message with id " + id, + new Long(expectedId), id); + expectedId++; + } + } + + /** + * Tests that messages in given range including dequeued one are deleted + * from the queue on invocation of + * {@link SimpleAMQQueue#removeMessagesFromQueue(long, long, StoreContext)} + */ + public void testRemoveMessagesFromQueueWithDequeuedEntry() + { + int messageNumber = 4; + int dequeueMessageIndex = 1; + + // put messages into a test queue + enqueueGivenNumberOfMessages(_queue, messageNumber); + + // dequeue message + dequeueMessage(_queue, dequeueMessageIndex); + + // remove messages + _queue.removeMessagesFromQueue(0, messageNumber); + + // get queue entries + List<QueueEntry> entries = _queue.getMessagesOnTheQueue(); + + // assert queue entries + assertNotNull("Null is returned from getMessagesOnTheQueue", entries); + assertEquals("Queue should be empty", 0, entries.size()); + } + + /** + * Tests that dequeued message on the top is not accounted and next message + * is deleted from the queue on invocation of + * {@link SimpleAMQQueue#deleteMessageFromTop(StoreContext)} + */ + public void testDeleteMessageFromTopWithDequeuedEntryOnTop() + { + int messageNumber = 4; + int dequeueMessageIndex = 0; + + // put messages into a test queue + enqueueGivenNumberOfMessages(_queue, messageNumber); + + // dequeue message on top + dequeueMessage(_queue, dequeueMessageIndex); + + //delete message from top + _queue.deleteMessageFromTop(); + + //get queue netries + List<QueueEntry> entries = _queue.getMessagesOnTheQueue(); + + // assert queue entries + assertNotNull("Null is returned from getMessagesOnTheQueue", entries); + assertEquals("Expected " + (messageNumber - 2) + " number of messages but recieved " + entries.size(), + messageNumber - 2, entries.size()); + assertEquals("Expected first entry with id 2", new Long(2), + ((AMQMessage) entries.get(0).getMessage()).getMessageId()); + } + + /** + * Tests that all messages including dequeued one are deleted from the queue + * on invocation of {@link SimpleAMQQueue#clearQueue(StoreContext)} + */ + public void testClearQueueWithDequeuedEntry() + { + int messageNumber = 4; + int dequeueMessageIndex = 1; + + // put messages into a test queue + enqueueGivenNumberOfMessages(_queue, messageNumber); + + // dequeue message on a test queue + dequeueMessage(_queue, dequeueMessageIndex); + + // clean queue + try + { + _queue.clearQueue(); + } + catch (AMQException e) + { + fail("Failure to clear queue:" + e.getMessage()); + } + + // get queue entries + List<QueueEntry> entries = _queue.getMessagesOnTheQueue(); + + // assert queue entries + assertNotNull(entries); + assertEquals(0, entries.size()); + } + + /** + * Tests whether dequeued entry is sent to subscriber in result of + * invocation of {@link SimpleAMQQueue#processQueue(QueueRunner)} + */ + public void testProcessQueueWithDequeuedEntry() + { + // total number of messages to send + int messageNumber = 4; + int dequeueMessageIndex = 1; + + // create queue with overridden method deliverAsync + SimpleAMQQueue testQueue = new SimpleAMQQueue(new AMQShortString("test"), false, + new AMQShortString("testOwner"), false, false, _virtualHost, null) + { + @Override + public void deliverAsync(Subscription sub) + { + // do nothing + } + }; + + // put messages + List<QueueEntry> entries = enqueueGivenNumberOfMessages(testQueue, messageNumber); + + // dequeue message + dequeueMessage(testQueue, dequeueMessageIndex); + + // latch to wait for message receipt + final CountDownLatch latch = new CountDownLatch(messageNumber -1); + + // create a subscription + MockSubscription subscription = new MockSubscription() + { + /** + * Send a message and decrement latch + */ + public void send(QueueEntry msg) throws AMQException + { + super.send(msg); + latch.countDown(); + } + }; + + try + { + // subscribe + testQueue.registerSubscription(subscription, false); + + // process queue + testQueue.processQueue(new QueueRunner(testQueue, 1) + { + public void run() + { + // do nothing + } + }); + } + catch (AMQException e) + { + fail("Failure to process queue:" + e.getMessage()); + } + // wait up to 1 minute for message receipt + try + { + latch.await(1, TimeUnit.MINUTES); + } + catch (InterruptedException e1) + { + Thread.currentThread().interrupt(); + } + List<QueueEntry> expected = createEntriesList(entries.get(0), entries.get(2), entries.get(3)); + verifyRecievedMessages(expected, subscription.getMessages()); + } + + /** + * Tests that entry in dequeued state are not enqueued and not delivered to subscription + */ + public void testEqueueDequeuedEntry() + { + // create a queue where each even entry is considered a dequeued + SimpleAMQQueue queue = new SimpleAMQQueue(new AMQShortString("test"), false, new AMQShortString("testOwner"), + false, false, _virtualHost, new QueueEntryListFactory() + { + public QueueEntryList createQueueEntryList(AMQQueue queue) + { + /** + * Override SimpleQueueEntryList to create a dequeued + * entries for messages with even id + */ + return new SimpleQueueEntryList(queue) + { + /** + * Entries with even message id are considered + * dequeued! + */ + protected QueueEntryImpl createQueueEntry(final ServerMessage message) + { + return new QueueEntryImpl(this, message) + { + public boolean isDequeued() + { + return (((AMQMessage) message).getMessageId().longValue() % 2 == 0); + } + + public boolean isDispensed() + { + return (((AMQMessage) message).getMessageId().longValue() % 2 == 0); + } + + public boolean isAvailable() + { + return !(((AMQMessage) message).getMessageId().longValue() % 2 == 0); + } + }; + } + }; + } + }, null); + // create a subscription + MockSubscription subscription = new MockSubscription(); + + // register subscription + try + { + queue.registerSubscription(subscription, false); + } + catch (AMQException e) + { + fail("Failure to register subscription:" + e.getMessage()); + } + + // put test messages into a queue + putGivenNumberOfMessages(queue, 4); + + // assert received messages + List<QueueEntry> messages = subscription.getMessages(); + assertEquals("Only 2 messages should be returned", 2, messages.size()); + assertEquals("ID of first message should be 1", new Long(1), + ((AMQMessage) messages.get(0).getMessage()).getMessageId()); + assertEquals("ID of second message should be 3", new Long(3), + ((AMQMessage) messages.get(1).getMessage()).getMessageId()); + } + + /** + * A helper method to create a queue with given name + * + * @param name + * queue name + * @return queue + */ + private SimpleAMQQueue createQueue(String name) + { + SimpleAMQQueue queue = null; + try + { + AMQShortString queueName = new AMQShortString(name); + AMQShortString ownerName = new AMQShortString(name + "Owner"); + queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(queueName, false, ownerName, false, false, + _virtualHost, _arguments); + } + catch (AMQException e) + { + fail("Failure to create a queue:" + e.getMessage()); + } + assertNotNull("Queue was not created", queue); + return queue; + } + + /** + * A helper method to put given number of messages into queue + * <p> + * All messages are asserted that they are present on queue + * + * @param queue + * queue to put messages into + * @param messageNumber + * number of messages to put into queue + */ + private List<QueueEntry> enqueueGivenNumberOfMessages(AMQQueue queue, int messageNumber) + { + putGivenNumberOfMessages(queue, messageNumber); + + // make sure that all enqueued messages are on the queue + List<QueueEntry> entries = queue.getMessagesOnTheQueue(); + assertEquals(messageNumber, entries.size()); + for (int i = 0; i < messageNumber; i++) + { + assertEquals(new Long(i), ((AMQMessage)entries.get(i).getMessage()).getMessageId()); + } + return entries; + } + + /** + * A helper method to put given number of messages into queue + * <p> + * Queue is not checked if messages are added into queue + * + * @param queue + * queue to put messages into + * @param messageNumber + * number of messages to put into queue + * @param queue + * @param messageNumber + */ + private void putGivenNumberOfMessages(AMQQueue queue, int messageNumber) + { + for (int i = 0; i < messageNumber; i++) + { + // Create message + Long messageId = new Long(i); + AMQMessage message = null; + try + { + message = createMessage(messageId); + } + catch (AMQException e) + { + fail("Failure to create a test message:" + e.getMessage()); + } + // Put message on queue + try + { + queue.enqueue(message); + } + catch (AMQException e) + { + fail("Failure to put message on queue:" + e.getMessage()); + } + } + } + + /** + * A helper method to dequeue an entry on queue with given index + * + * @param queue + * queue to dequeue message on + * @param dequeueMessageIndex + * entry index to dequeue. + */ + private QueueEntry dequeueMessage(AMQQueue queue, int dequeueMessageIndex) + { + List<QueueEntry> entries = queue.getMessagesOnTheQueue(); + QueueEntry entry = entries.get(dequeueMessageIndex); + entry.acquire(); + entry.dequeue(); + assertTrue(entry.isDequeued()); + return entry; + } + + private List<QueueEntry> createEntriesList(QueueEntry... entries) + { + ArrayList<QueueEntry> entriesList = new ArrayList<QueueEntry>(); + for (QueueEntry entry : entries) + { + entriesList.add(entry); + } + return entriesList; + } + + private void verifyRecievedMessages(List<QueueEntry> expected, + List<QueueEntry> delivered) + { + assertEquals("Consumer did not receive the expected number of messages", + expected.size(), delivered.size()); + + for (QueueEntry msg : expected) + { + assertTrue("Consumer did not recieve msg: " + + msg.getMessage().getMessageNumber(), delivered.contains(msg)); + } + } + public class TestMessage extends AMQMessage { private final long _tag; @@ -747,4 +1327,20 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase AMQMessage messageA = new TestMessage(id, id, info); return messageA; } + + class TestSimpleQueueEntryListFactory implements QueueEntryListFactory + { + QueueEntryList _list; + + public QueueEntryList createQueueEntryList(AMQQueue queue) + { + _list = new SimpleQueueEntryList(queue); + return _list; + } + + public QueueEntryList getQueueEntryList() + { + return _list; + } + } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java index 320a75045a..7136f07ca5 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.queue; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.apache.qpid.AMQException; import org.apache.qpid.server.message.AMQMessage; import junit.framework.TestCase; @@ -155,5 +156,55 @@ public class SimpleQueueEntryListTest extends TestCase assertEquals("Count should have been equal",count,remainingMessages.size()); } - + + public void testDequedMessagedNotPresentInIterator() + { + int numberOfMessages = 10; + SimpleQueueEntryList entryList = new SimpleQueueEntryList(new MockAMQQueue("test")); + QueueEntry[] entries = new QueueEntry[numberOfMessages]; + + for(int i = 0; i < numberOfMessages ; i++) + { + AMQMessage message = null;; + try + { + message = new MockAMQMessage(i); + } + catch (AMQException e) + { + fail("Failure to create a mock message:" + e.getMessage()); + } + QueueEntry entry = entryList.add(message); + assertNotNull("QE should not be null", entry); + entries[i]= entry; + } + + // dequeue all even messages + for (QueueEntry queueEntry : entries) + { + long i = ((AMQMessage)queueEntry.getMessage()).getMessageId().longValue(); + if (i%2 == 0) + { + queueEntry.acquire(); + queueEntry.dequeue(); + } + } + + // iterate and check that dequeued messages are not returned by iterator + QueueEntryIterator it = entryList.iterator(); + int counter = 0; + int i = 1; + while (it.advance()) + { + QueueEntry entry = it.getNode(); + Long id = ((AMQMessage)entry.getMessage()).getMessageId(); + assertEquals("Expected message with id " + i + " but got message with id " + + id, new Long(i), id); + counter++; + i += 2; + } + int expectedNumber = numberOfMessages / 2; + assertEquals("Expected " + expectedNumber + " number of entries in iterator but got " + counter, + expectedNumber, counter); + } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java new file mode 100644 index 0000000000..b10442d7db --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java @@ -0,0 +1,358 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.auth.manager; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.security.Provider; +import java.security.Security; + +import javax.security.auth.Subject; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.util.InternalBrokerBaseCase; + +/** + * + * Tests the public methods of PrincipalDatabaseAuthenticationManager. + * + */ +public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBaseCase +{ + private AuthenticationManager _manager = null; // Class under test + private String TEST_USERNAME = "guest"; + private String TEST_PASSWORD = "guest"; + + /** + * @see org.apache.qpid.server.util.InternalBrokerBaseCase#tearDown() + */ + @Override + public void tearDown() throws Exception + { + super.tearDown(); + if (_manager != null) + { + _manager.close(); + } + } + + /** + * @see org.apache.qpid.server.util.InternalBrokerBaseCase#setUp() + */ + @Override + public void setUp() throws Exception + { + super.setUp(); + + final String passwdFilename = createPasswordFile().getCanonicalPath(); + final ConfigurationPlugin config = getConfig(PlainPasswordFilePrincipalDatabase.class.getName(), + "passwordFile", passwdFilename); + + _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(config); + } + + /** + * Tests where the case where the config specifies a PD implementation + * that is not found. + */ + public void testPrincipalDatabaseImplementationNotFound() throws Exception + { + try + { + _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(getConfig("not.Found", null, null)); + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + // PASS + } + } + + /** + * Tests where the case where the config specifies a PD implementation + * of the wrong type. + */ + public void testPrincipalDatabaseImplementationWrongType() throws Exception + { + try + { + _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(getConfig(String.class.getName(), null, null)); // Not a PrincipalDatabase implementation + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + // PASS + } + } + + /** + * Tests the case where a setter with the desired name cannot be found. + */ + public void testPrincipalDatabaseSetterNotFound() throws Exception + { + try + { + _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(getConfig(PlainPasswordFilePrincipalDatabase.class.getName(), "noMethod", "test")); + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + // PASS + } + } + + /** + * QPID-1347. Make sure the exception message and stack trace is reasonable for an absent password file. + */ + public void testPrincipalDatabaseThrowsSetterFileNotFound() throws Exception + { + try + { + _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(getConfig(PlainPasswordFilePrincipalDatabase.class.getName(), "passwordFile", "/not/found")); + fail("Exception not thrown"); + } + catch (ConfigurationException ce) + { + // PASS + assertNotNull("Expected an underlying cause", ce.getCause()); + assertEquals(FileNotFoundException.class, ce.getCause().getClass()); + } + } + + /** + * Tests that the PDAM registers SASL mechanisms correctly with the runtime. + */ + public void testRegisteredMechanisms() throws Exception + { + assertNotNull(_manager.getMechanisms()); + // relies on those mechanisms attached to PropertiesPrincipalDatabaseManager + assertEquals("AMQPLAIN PLAIN CRAM-MD5", _manager.getMechanisms()); + + Provider qpidProvider = Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME); + assertNotNull(qpidProvider); + } + + /** + * Tests that the SASL factory method createSaslServer correctly + * returns a non-null implementation. + */ + public void testSaslMechanismCreation() throws Exception + { + SaslServer server = _manager.createSaslServer("CRAM-MD5", "localhost"); + assertNotNull(server); + // Merely tests the creation of the mechanism. Mechanisms themselves are tested + // by their own tests. + } + + /** + * Tests that the authenticate method correctly interprets an + * authentication success. + * + */ + public void testSaslAuthenticationSuccess() throws Exception + { + SaslServer testServer = createTestSaslServer(true, false); + + AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes()); + final Subject subject = result.getSubject(); + assertTrue(subject.getPrincipals().contains(new UsernamePrincipal("guest"))); + assertEquals(AuthenticationStatus.SUCCESS, result.getStatus()); + } + + /** + * + * Tests that the authenticate method correctly interprets an + * authentication not complete. + * + */ + public void testSaslAuthenticationNotCompleted() throws Exception + { + SaslServer testServer = createTestSaslServer(false, false); + + AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes()); + assertNull(result.getSubject()); + assertEquals(AuthenticationStatus.CONTINUE, result.getStatus()); + } + + /** + * + * Tests that the authenticate method correctly interprets an + * authentication error. + * + */ + public void testSaslAuthenticationError() throws Exception + { + SaslServer testServer = createTestSaslServer(false, true); + + AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes()); + assertNull(result.getSubject()); + assertEquals(AuthenticationStatus.ERROR, result.getStatus()); + } + + /** + * Tests that the authenticate method correctly interprets an + * authentication success. + * + */ + public void testNonSaslAuthenticationSuccess() throws Exception + { + AuthenticationResult result = _manager.authenticate("guest", "guest"); + final Subject subject = result.getSubject(); + assertFalse("Subject should not be set read-only", subject.isReadOnly()); + assertTrue(subject.getPrincipals().contains(new UsernamePrincipal("guest"))); + assertEquals(AuthenticationStatus.SUCCESS, result.getStatus()); + } + + /** + * Tests that the authenticate method correctly interprets an + * authentication success. + * + */ + public void testNonSaslAuthenticationNotCompleted() throws Exception + { + AuthenticationResult result = _manager.authenticate("guest", "wrongpassword"); + assertNull(result.getSubject()); + assertEquals(AuthenticationStatus.CONTINUE, result.getStatus()); + } + + /** + * Tests the ability to de-register the provider. + */ + public void testClose() throws Exception + { + assertEquals("AMQPLAIN PLAIN CRAM-MD5", _manager.getMechanisms()); + assertNotNull(Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME)); + + _manager.close(); + + // Check provider has been removed. + assertNull(_manager.getMechanisms()); + assertNull(Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME)); + _manager = null; + } + + /** + * Test SASL implementation used to test the authenticate() method. + */ + private SaslServer createTestSaslServer(final boolean complete, final boolean throwSaslException) + { + return new SaslServer() + { + public String getMechanismName() + { + return null; + } + + public byte[] evaluateResponse(byte[] response) throws SaslException + { + if (throwSaslException) + { + throw new SaslException("Mocked exception"); + } + return null; + } + + public boolean isComplete() + { + return complete; + } + + public String getAuthorizationID() + { + return complete ? "guest" : null; + } + + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + return null; + } + + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + return null; + } + + public Object getNegotiatedProperty(String propName) + { + return null; + } + + public void dispose() throws SaslException + { + } + }; + } + + private ConfigurationPlugin getConfig(final String clazz, final String argName, final String argValue) throws Exception + { + final ConfigurationPlugin config = new PrincipalDatabaseAuthenticationManager.PrincipalDatabaseAuthenticationManagerConfiguration(); + + XMLConfiguration xmlconfig = new XMLConfiguration(); + xmlconfig.addProperty("pd-auth-manager.principal-database.class", clazz); + + if (argName != null) + { + xmlconfig.addProperty("pd-auth-manager.principal-database.attributes.attribute.name", argName); + xmlconfig.addProperty("pd-auth-manager.principal-database.attributes.attribute.value", argValue); + } + + // Create a CompositeConfiguration as this is what the broker uses + CompositeConfiguration composite = new CompositeConfiguration(); + composite.addConfiguration(xmlconfig); + config.setConfiguration("security", xmlconfig); + return config; + } + + private File createPasswordFile() throws Exception + { + BufferedWriter writer = null; + try + { + File testFile = File.createTempFile(this.getClass().getName(),"tmp"); + testFile.deleteOnExit(); + + writer = new BufferedWriter(new FileWriter(testFile)); + writer.write(TEST_USERNAME + ":" + TEST_PASSWORD); + writer.newLine(); + + return testFile; + + } + finally + { + if (writer != null) + { + writer.close(); + } + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java index e8c24da68d..6dc7b19d3d 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java @@ -20,188 +20,125 @@ */ package org.apache.qpid.server.security.auth.rmi; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; import java.util.Collections; import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; - -import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase; -import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; import junit.framework.TestCase; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; + +/** + * Tests the RMIPasswordAuthenticator and its collaboration with the AuthenticationManager. + * + */ public class RMIPasswordAuthenticatorTest extends TestCase { private final String USERNAME = "guest"; private final String PASSWORD = "guest"; - private final String B64_MD5HASHED_PASSWORD = "CE4DQ6BIb/BVMN9scFyLtA=="; private RMIPasswordAuthenticator _rmipa; - - private Base64MD5PasswordFilePrincipalDatabase _md5Pd; - private File _md5PwdFile; - - private PlainPasswordFilePrincipalDatabase _plainPd; - private File _plainPwdFile; - - private Subject testSubject; + private String[] _credentials; protected void setUp() throws Exception { _rmipa = new RMIPasswordAuthenticator(); - _md5Pd = new Base64MD5PasswordFilePrincipalDatabase(); - _md5PwdFile = createTempPasswordFile(this.getClass().getName()+"md5pwd", USERNAME, B64_MD5HASHED_PASSWORD); - _md5Pd.setPasswordFile(_md5PwdFile.getAbsolutePath()); - - _plainPd = new PlainPasswordFilePrincipalDatabase(); - _plainPwdFile = createTempPasswordFile(this.getClass().getName()+"plainpwd", USERNAME, PASSWORD); - _plainPd.setPasswordFile(_plainPwdFile.getAbsolutePath()); - - testSubject = new Subject(true, + _credentials = new String[] {USERNAME, PASSWORD}; + } + + /** + * Tests a successful authentication. Ensures that a populated read-only subject it returned. + */ + public void testAuthenticationSuccess() + { + final Subject expectedSubject = new Subject(true, Collections.singleton(new JMXPrincipal(USERNAME)), Collections.EMPTY_SET, Collections.EMPTY_SET); - } - - private File createTempPasswordFile(String filenamePrefix, String user, String password) - { - try - { - File testFile = File.createTempFile(filenamePrefix,"tmp"); - testFile.deleteOnExit(); - - BufferedWriter writer = new BufferedWriter(new FileWriter(testFile)); - writer.write(user + ":" + password); - writer.newLine(); + _rmipa.setAuthenticationManager(createTestAuthenticationManager(true, null)); - writer.flush(); - writer.close(); - return testFile; - } - catch (IOException e) - { - fail("Unable to create temporary test password file." + e.getMessage()); - } + Subject newSubject = _rmipa.authenticate(_credentials); + assertTrue("Subject must be readonly", newSubject.isReadOnly()); + assertTrue("Returned subject does not equal expected value", + newSubject.equals(expectedSubject)); - return null; } - - - //********** Test Methods *********// - - public void testAuthenticate() + /** + * Tests a unsuccessful authentication. + */ + public void testUsernameOrPasswordInvalid() { - String[] credentials; - Subject newSubject; - - // Test when no PD has been set - try - { - credentials = new String[]{USERNAME, PASSWORD}; - newSubject = _rmipa.authenticate(credentials); - fail("SecurityException expected due to lack of principal database"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.UNABLE_TO_LOOKUP, se.getMessage()); - } - - //The PrincipalDatabase's are tested primarily by their own tests, but - //minimal tests are done here to exercise their usage in this area. + _rmipa.setAuthenticationManager(createTestAuthenticationManager(false, null)); - // Test correct passwords are verified with an MD5 PD - try - { - _rmipa.setPrincipalDatabase(_md5Pd); - credentials = new String[]{USERNAME, PASSWORD}; - newSubject = _rmipa.authenticate(credentials); - assertTrue("Returned subject does not equal expected value", - newSubject.equals(testSubject)); - } - catch (Exception e) - { - fail("Unexpected Exception:" + e.getMessage()); - } - - // Test incorrect passwords are not verified with an MD5 PD try { - credentials = new String[]{USERNAME, PASSWORD+"incorrect"}; - newSubject = _rmipa.authenticate(credentials); - fail("SecurityException expected due to incorrect password"); + _rmipa.authenticate(_credentials); + fail("Exception not thrown"); } catch (SecurityException se) { assertEquals("Unexpected exception message", RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); - } - - // Test non-existent accounts are not verified with an MD5 PD - try - { - credentials = new String[]{USERNAME+"invalid", PASSWORD}; - newSubject = _rmipa.authenticate(credentials); - fail("SecurityException expected due to non-existant account"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); - } - // Test correct passwords are verified with a Plain PD - try - { - _rmipa.setPrincipalDatabase(_plainPd); - credentials = new String[]{USERNAME, PASSWORD}; - newSubject = _rmipa.authenticate(credentials); - assertTrue("Returned subject does not equal expected value", - newSubject.equals(testSubject)); - } - catch (Exception e) - { - fail("Unexpected Exception"); } + } + + /** + * Tests case where authentication system itself fails. + */ + public void testAuthenticationFailure() + { + final Exception mockAuthException = new Exception("Mock Auth system failure"); + _rmipa.setAuthenticationManager(createTestAuthenticationManager(false, mockAuthException)); - // Test incorrect passwords are not verified with a Plain PD try { - credentials = new String[]{USERNAME, PASSWORD+"incorrect"}; - newSubject = _rmipa.authenticate(credentials); - fail("SecurityException expected due to incorrect password"); + _rmipa.authenticate(_credentials); + fail("Exception not thrown"); } catch (SecurityException se) { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); + assertEquals("Initial cause not found", mockAuthException, se.getCause()); } - - // Test non-existent accounts are not verified with an Plain PD + } + + + /** + * Tests case where authentication manager is not set. + */ + public void testNullAuthenticationManager() + { try { - credentials = new String[]{USERNAME+"invalid", PASSWORD}; - newSubject = _rmipa.authenticate(credentials); - fail("SecurityException expected due to non existant account"); + _rmipa.authenticate(_credentials); + fail("SecurityException expected due to lack of authentication manager"); } catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); + RMIPasswordAuthenticator.UNABLE_TO_LOOKUP, se.getMessage()); } + } + /** + * Tests case where arguments are non-Strings.. + */ + public void testWithNonStringArrayArgument() + { // Test handling of non-string credential's + final Object[] objCredentials = new Object[]{USERNAME, PASSWORD}; try { - Object[] objCredentials = new Object[]{USERNAME, PASSWORD}; - newSubject = _rmipa.authenticate(objCredentials); + _rmipa.authenticate(objCredentials); fail("SecurityException expected due to non string[] credentials"); } catch (SecurityException se) @@ -209,12 +146,18 @@ public class RMIPasswordAuthenticatorTest extends TestCase assertEquals("Unexpected exception message", RMIPasswordAuthenticator.SHOULD_BE_STRING_ARRAY, se.getMessage()); } - - // Test handling of incorrect number of credential's + } + + /** + * Tests case where there are too many, too few or null arguments. + */ + public void testWithIllegalNumberOfArguments() + { + // Test handling of incorrect number of credentials try { - credentials = new String[]{USERNAME, PASSWORD, PASSWORD}; - newSubject = _rmipa.authenticate(credentials); + _credentials = new String[]{USERNAME, PASSWORD, PASSWORD}; + _rmipa.authenticate(_credentials); fail("SecurityException expected due to supplying wrong number of credentials"); } catch (SecurityException se) @@ -223,12 +166,12 @@ public class RMIPasswordAuthenticatorTest extends TestCase RMIPasswordAuthenticator.SHOULD_HAVE_2_ELEMENTS, se.getMessage()); } - // Test handling of null credential's + // Test handling of null credentials try { //send a null array - credentials = null; - newSubject = _rmipa.authenticate(credentials); + _credentials = null; + _rmipa.authenticate(_credentials); fail("SecurityException expected due to not supplying an array of credentials"); } catch (SecurityException se) @@ -240,8 +183,8 @@ public class RMIPasswordAuthenticatorTest extends TestCase try { //send a null password - credentials = new String[]{USERNAME, null}; - newSubject = _rmipa.authenticate(credentials); + _credentials = new String[]{USERNAME, null}; + _rmipa.authenticate(_credentials); fail("SecurityException expected due to sending a null password"); } catch (SecurityException se) @@ -253,8 +196,8 @@ public class RMIPasswordAuthenticatorTest extends TestCase try { //send a null username - credentials = new String[]{null, PASSWORD}; - newSubject = _rmipa.authenticate(credentials); + _credentials = new String[]{null, PASSWORD}; + _rmipa.authenticate(_credentials); fail("SecurityException expected due to sending a null username"); } catch (SecurityException se) @@ -264,4 +207,54 @@ public class RMIPasswordAuthenticatorTest extends TestCase } } + private AuthenticationManager createTestAuthenticationManager(final boolean successfulAuth, final Exception exception) + { + return new AuthenticationManager() + { + public void configure(ConfigurationPlugin config) + { + throw new UnsupportedOperationException(); + } + + public void initialise() + { + throw new UnsupportedOperationException(); + } + + public void close() + { + throw new UnsupportedOperationException(); + } + + public String getMechanisms() + { + throw new UnsupportedOperationException(); + } + + public SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException + { + throw new UnsupportedOperationException(); + } + + public AuthenticationResult authenticate(SaslServer server, byte[] response) + { + throw new UnsupportedOperationException(); + } + + public AuthenticationResult authenticate(String username, String password) + { + if (exception != null) { + return new AuthenticationResult(AuthenticationStatus.ERROR, exception); + } + else if (successfulAuth) + { + return new AuthenticationResult(new Subject()); + } + else + { + return new AuthenticationResult(AuthenticationStatus.CONTINUE); + } + } + }; + } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexServerTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexServerTest.java new file mode 100644 index 0000000000..86e4e23750 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexServerTest.java @@ -0,0 +1,228 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.security.auth.sasl; + +import java.io.File; +import java.io.IOException; +import java.security.MessageDigest; +import java.security.Principal; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.security.auth.login.AccountNotFoundException; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import junit.framework.TestCase; + +import org.apache.commons.codec.binary.Hex; +import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexInitialiser; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexSaslServer; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexServerFactory; + +/** + * Test for the CRAM-MD5-HEX SASL mechanism. + * + * This test case focuses on testing {@link CRAMMD5HexSaslServer} but also exercises + * collaborators {@link CRAMMD5HexInitialiser} and {@link Base64MD5PasswordFilePrincipalDatabase} + */ +public class CRAMMD5HexServerTest extends TestCase +{ + + private SaslServer _saslServer; // Class under test + private CRAMMD5HexServerFactory _saslFactory; + + @Override + protected void setUp() throws Exception + { + super.setUp(); + + CRAMMD5HexInitialiser _initializer = new CRAMMD5HexInitialiser(); + + //Use properties to create a PrincipalDatabase + Base64MD5PasswordFilePrincipalDatabase db = createTestPrincipalDatabase(); + assertEquals("Unexpected number of test users in the db", 2, db.getUsers().size()); + + _initializer.initialise(db); + + _saslFactory = new CRAMMD5HexServerFactory(); + + _saslServer = _saslFactory.createSaslServer(CRAMMD5HexSaslServer.MECHANISM, + "AMQP", + "localhost", + _initializer.getProperties(), + _initializer.getCallbackHandler()); + assertNotNull("Unable to create saslServer with mechanism type " + CRAMMD5HexSaslServer.MECHANISM, _saslServer); + + } + + public void testSuccessfulAuth() throws Exception + { + + final byte[] serverChallenge = _saslServer.evaluateResponse(new byte[0]); + + // Generate client response + final byte[] clientResponse = generateClientResponse("knownuser", "guest", serverChallenge); + + + byte[] nextServerChallenge = _saslServer.evaluateResponse(clientResponse); + assertTrue("Exchange must be flagged as complete after successful authentication", _saslServer.isComplete()); + assertNull("Next server challenge must be null after successful authentication", nextServerChallenge); + + } + + public void testKnownUserPresentsWrongPassword() throws Exception + { + byte[] serverChallenge = _saslServer.evaluateResponse(new byte[0]); + + + final byte[] clientResponse = generateClientResponse("knownuser", "wrong!", serverChallenge); + try + { + _saslServer.evaluateResponse(clientResponse); + fail("Exception not thrown"); + } + catch (SaslException se) + { + // PASS + } + assertFalse("Exchange must not be flagged as complete after unsuccessful authentication", _saslServer.isComplete()); + } + + public void testUnknownUser() throws Exception + { + final byte[] serverChallenge = _saslServer.evaluateResponse(new byte[0]); + + + final byte[] clientResponse = generateClientResponse("unknownuser", "guest", serverChallenge); + + try + { + _saslServer.evaluateResponse(clientResponse); + fail("Exception not thrown"); + } + catch (SaslException se) + { + assertExceptionHasUnderlyingAsCause(AccountNotFoundException.class, se); + // PASS + } + assertFalse("Exchange must not be flagged as complete after unsuccessful authentication", _saslServer.isComplete()); + } + + /** + * + * Demonstrates QPID-3158. A defect meant that users with some valid password were failing to + * authenticate when using the .NET 0-8 client (uses this SASL mechanism). + * It so happens that password "guest2" was one of the affected passwords. + * + * @throws Exception + */ + public void testSuccessfulAuthReproducingQpid3158() throws Exception + { + byte[] serverChallenge = _saslServer.evaluateResponse(new byte[0]); + + // Generate client response + byte[] resp = generateClientResponse("qpid3158user", "guest2", serverChallenge); + + byte[] nextServerChallenge = _saslServer.evaluateResponse(resp); + assertTrue("Exchange must be flagged as complete after successful authentication", _saslServer.isComplete()); + assertNull("Next server challenge must be null after successful authentication", nextServerChallenge); + } + + /** + * Since we don't have a CRAM-MD5-HEX implementation client implementation in Java, this method + * provides the implementation for first principals. + * + * @param userId user id + * @param clearTextPassword clear text password + * @param serverChallenge challenge from server + * + * @return challenge response + */ + private byte[] generateClientResponse(final String userId, final String clearTextPassword, final byte[] serverChallenge) throws Exception + { + byte[] digestedPasswordBytes = MessageDigest.getInstance("MD5").digest(clearTextPassword.getBytes()); + char[] hexEncodedDigestedPassword = Hex.encodeHex(digestedPasswordBytes); + byte[] hexEncodedDigestedPasswordBytes = new String(hexEncodedDigestedPassword).getBytes(); + + + Mac hmacMd5 = Mac.getInstance("HmacMD5"); + hmacMd5.init(new SecretKeySpec(hexEncodedDigestedPasswordBytes, "HmacMD5")); + final byte[] messageAuthenticationCode = hmacMd5.doFinal(serverChallenge); + + // Build client response + String responseAsString = userId + " " + new String(Hex.encodeHex(messageAuthenticationCode)); + byte[] resp = responseAsString.getBytes(); + return resp; + } + + /** + * Creates a test principal database. + * + * @return + * @throws IOException + */ + private Base64MD5PasswordFilePrincipalDatabase createTestPrincipalDatabase() throws IOException + { + Base64MD5PasswordFilePrincipalDatabase db = new Base64MD5PasswordFilePrincipalDatabase(); + File file = File.createTempFile("passwd", "db"); + file.deleteOnExit(); + db.setPasswordFile(file.getCanonicalPath()); + db.createPrincipal( createTestPrincipal("knownuser"), "guest".toCharArray()); + db.createPrincipal( createTestPrincipal("qpid3158user"), "guest2".toCharArray()); + return db; + } + + private Principal createTestPrincipal(final String name) + { + return new Principal() + { + public String getName() + { + return name; + } + }; + } + + private void assertExceptionHasUnderlyingAsCause(final Class<? extends Throwable> expectedUnderlying, Throwable e) + { + assertNotNull(e); + int infiniteLoopGuard = 0; // Guard against loops in the cause chain + boolean foundExpectedUnderlying = false; + while (e.getCause() != null && infiniteLoopGuard++ < 10) + { + if (expectedUnderlying.equals(e.getCause().getClass())) + { + foundExpectedUnderlying = true; + break; + } + e = e.getCause(); + } + + if (!foundExpectedUnderlying) + { + fail("Not found expected underlying exception " + expectedUnderlying + " as underlying cause of " + e.getClass()); + } + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipalTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipalTest.java new file mode 100644 index 0000000000..076b7c9248 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipalTest.java @@ -0,0 +1,86 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl; + +import junit.framework.TestCase; + +public class GroupPrincipalTest extends TestCase +{ + public void testGetName() + { + final GroupPrincipal principal = new GroupPrincipal("group"); + assertEquals("group", principal.getName()); + } + + public void testAddRejected() + { + final GroupPrincipal principal = new GroupPrincipal("group"); + final UsernamePrincipal user = new UsernamePrincipal("name"); + + try + { + principal.addMember(user); + fail("Exception not thrown"); + } + catch (UnsupportedOperationException uso) + { + // PASS + } + } + + public void testEqualitySameName() + { + final String string = "string"; + final GroupPrincipal principal1 = new GroupPrincipal(string); + final GroupPrincipal principal2 = new GroupPrincipal(string); + assertTrue(principal1.equals(principal2)); + } + + public void testEqualityEqualName() + { + final GroupPrincipal principal1 = new GroupPrincipal(new String("string")); + final GroupPrincipal principal2 = new GroupPrincipal(new String("string")); + assertTrue(principal1.equals(principal2)); + } + + public void testInequalityDifferentGroupPrincipals() + { + GroupPrincipal principal1 = new GroupPrincipal("string1"); + GroupPrincipal principal2 = new GroupPrincipal("string2"); + assertFalse(principal1.equals(principal2)); + } + + public void testInequalityNonGroupPrincipal() + { + GroupPrincipal principal = new GroupPrincipal("string"); + assertFalse(principal.equals(new UsernamePrincipal("string"))); + } + + public void testInequalityNull() + { + GroupPrincipal principal = new GroupPrincipal("string"); + assertFalse(principal.equals(null)); + } + + + + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalUtils.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalUtils.java new file mode 100644 index 0000000000..8b9b2df5a3 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalUtils.java @@ -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. + * + */ +package org.apache.qpid.server.security.auth.sasl; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import javax.security.auth.Subject; + +public class TestPrincipalUtils +{ + + /** + * Creates a test subject, with exactly one UsernamePrincipal and zero or more GroupPrincipals. + */ + public static Subject createTestSubject(final String username, final String... groups) + { + final Set<Principal> principals = new HashSet<Principal>(1 + groups.length); + principals.add(new UsernamePrincipal(username)); + for (String group : groups) + { + principals.add(new GroupPrincipal(group)); + } + + final Subject subject = new Subject(true, principals, Collections.EMPTY_SET, Collections.EMPTY_SET); + return subject; + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipalTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipalTest.java new file mode 100644 index 0000000000..541f14d923 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipalTest.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.server.security.auth.sasl; + +import java.security.Principal; +import javax.security.auth.Subject; +import junit.framework.TestCase; + +/** + * Tests the UsernamePrincipal. + * + */ +public class UsernamePrincipalTest extends TestCase +{ + public void testEqualitySameObject() + { + final UsernamePrincipal principal = new UsernamePrincipal("string"); + assertTrue(principal.equals(principal)); + } + + public void testEqualitySameName() + { + final String string = "string"; + final UsernamePrincipal principal1 = new UsernamePrincipal(string); + final UsernamePrincipal principal2 = new UsernamePrincipal(string); + assertTrue(principal1.equals(principal2)); + } + + public void testEqualityEqualName() + { + final UsernamePrincipal principal1 = new UsernamePrincipal(new String("string")); + final UsernamePrincipal principal2 = new UsernamePrincipal(new String("string")); + assertTrue(principal1.equals(principal2)); + } + + public void testInequalityDifferentUserPrincipals() + { + UsernamePrincipal principal1 = new UsernamePrincipal("string1"); + UsernamePrincipal principal2 = new UsernamePrincipal("string2"); + assertFalse(principal1.equals(principal2)); + } + + public void testInequalityNonUserPrincipal() + { + UsernamePrincipal principal = new UsernamePrincipal("string"); + assertFalse(principal.equals(new String("string"))); + } + + public void testInequalityNull() + { + UsernamePrincipal principal = new UsernamePrincipal("string"); + assertFalse(principal.equals(null)); + } + + public void testGetUsernamePrincipalFromSubject() + { + final UsernamePrincipal expected = new UsernamePrincipal("name"); + final Principal other = new Principal() + { + public String getName() + { + return "otherprincipal"; + } + }; + + final Subject subject = new Subject(); + subject.getPrincipals().add(expected); + subject.getPrincipals().add(other); + + final UsernamePrincipal actual = UsernamePrincipal.getUsernamePrincipalFromSubject(subject); + assertSame(expected, actual); + } + + public void testUsernamePrincipalNotInSubject() + { + try + { + UsernamePrincipal.getUsernamePrincipalFromSubject(new Subject()); + fail("Exception not thrown"); + } + catch (IllegalArgumentException iae) + { + // PASS + } + } + + public void testTooManyUsernamePrincipalInSubject() + { + final Subject subject = new Subject(); + subject.getPrincipals().add(new UsernamePrincipal("name1")); + subject.getPrincipals().add(new UsernamePrincipal("name2")); + try + { + + UsernamePrincipal.getUsernamePrincipalFromSubject(subject); + fail("Exception not thrown"); + } + catch (IllegalArgumentException iae) + { + // PASS + } + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/signal/SignalHandlerTaskTest.java b/java/broker/src/test/java/org/apache/qpid/server/signal/SignalHandlerTaskTest.java new file mode 100644 index 0000000000..886cb080aa --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/signal/SignalHandlerTaskTest.java @@ -0,0 +1,118 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.signal; + +import java.lang.management.ManagementFactory; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; +import org.apache.qpid.test.utils.QpidTestCase; + +public class SignalHandlerTaskTest extends QpidTestCase +{ + private static final Logger LOGGER = Logger.getLogger(SignalHandlerTaskTest.class); + private static final String SUN_MISC_SIGNAL_CLASS = "sun.misc.Signal"; + private static final String SUN_MISC_SIGNAL_HANDLER_CLASS = "sun.misc.SignalHandler"; + + protected void setUp() throws Exception + { + super.setUp(); + } + + public void testSignalHandlerTask() throws Exception + { + final boolean expectedResult = classifyExpectedRegistrationResult(); + final int pid = getPID(); + final CountDownLatch latch = new CountDownLatch(1); + + SignalHandlerTask hupReparseTask = new SignalHandlerTask() + { + public void handle() + { + latch.countDown(); + LOGGER.info("Signal handled, latch decremented"); + } + }; + + assertEquals("Unexpected result trying to register Signal handler", + expectedResult, hupReparseTask.register("HUP")); + LOGGER.info("Signal handler was registered"); + + assertEquals("unexpected count for the latch", 1, latch.getCount()); + + if(expectedResult) + { + //registration succeeded as expected, so now + //send SIGHUP and verify the handler was run + String cmd = "/bin/kill -SIGHUP " + pid; + + LOGGER.info("Sending SIGHUP"); + Runtime.getRuntime().exec(cmd); + + assertTrue("HUP Signal was not handled in the allowed timeframe", + latch.await(5, TimeUnit.SECONDS)); + } + } + + public void testGetPlatformDescription() throws Exception + { + assertNotNull(SignalHandlerTask.getPlatformDescription()); + } + + private boolean classifyExpectedRegistrationResult() + { + String os = System.getProperty("os.name"); + if(String.valueOf(os).toLowerCase().contains("windows")) + { + //Windows does not support SIGHUP so registration will fail + LOGGER.info("Running on windows, so we expect SIGHUP handler registration to fail"); + return false; + } + + //otherwise, if the signal handler classes are present we would expect + //registration to succeed + boolean classesPresent = true; + try + { + Class.forName(SUN_MISC_SIGNAL_CLASS); + Class.forName(SUN_MISC_SIGNAL_HANDLER_CLASS); + LOGGER.info("Signal handling classes were present so we expect SIGHUP handler registration to succeed"); + } + catch (ClassNotFoundException cnfe) + { + classesPresent = false; + } + + return classesPresent; + } + + private int getPID() + { + String processName = ManagementFactory.getRuntimeMXBean().getName(); + + int pid = Integer.parseInt(processName.substring(0,processName.indexOf('@'))); + LOGGER.info("PID was determined to be " + pid); + + return pid; + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java b/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java new file mode 100644 index 0000000000..fbaa1342c9 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java @@ -0,0 +1,144 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.stats; + +import junit.framework.TestCase; + +/** + * Unit tests for the {@link StatisticsCounter} class. + */ +public class StatisticsCounterTest extends TestCase +{ + /** + * Check that statistics counters are created correctly. + */ + public void testCreate() + { + long before = System.currentTimeMillis(); + StatisticsCounter counter = new StatisticsCounter("name", 1234L); + long after = System.currentTimeMillis(); + + assertTrue(before <= counter.getStart()); + assertTrue(after >= counter.getStart()); + assertTrue(counter.getName().startsWith("name-")); + assertEquals(1234L, counter.getPeriod()); + } + + /** + * Check that totals add up correctly. + */ + public void testTotal() + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + long start = counter.getStart(); + for (int i = 0; i < 100; i++) + { + counter.registerEvent(i, start + i); + } + assertEquals(99 * 50, counter.getTotal()); // cf. Gauss + } + + /** + * Test totals add up correctly even when messages are delivered + * out-of-order. + */ + public void testTotalOutOfOrder() + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + long start = counter.getStart(); + assertEquals(0, counter.getTotal()); + counter.registerEvent(10, start + 2500); + assertEquals(10, counter.getTotal()); + counter.registerEvent(20, start + 1500); + assertEquals(30, counter.getTotal()); + counter.registerEvent(10, start + 500); + assertEquals(40, counter.getTotal()); + } + + /** + * Test that the peak rate is reported correctly. + */ + public void testPeak() throws Exception + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + long start = counter.getStart(); + assertEquals(0.0, counter.getPeak()); + Thread.sleep(500); + counter.registerEvent(1000, start + 500); + Thread.sleep(1000); + assertEquals(1000.0, counter.getPeak()); + counter.registerEvent(2000, start + 1500); + Thread.sleep(1000); + assertEquals(2000.0, counter.getPeak()); + counter.registerEvent(1000, start + 2500); + Thread.sleep(1000); + assertEquals(2000.0, counter.getPeak()); + } + + /** + * Test that peak rate is reported correctly for out-of-order messages, + * and the total is also unaffected. + */ + public void testPeakOutOfOrder() throws Exception + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + long start = counter.getStart(); + assertEquals(0.0, counter.getPeak()); + counter.registerEvent(1000, start + 2500); + Thread.sleep(1500); + assertEquals(0.0, counter.getPeak()); + counter.registerEvent(2000, start + 1500); + Thread.sleep(1000L); + assertEquals(0.0, counter.getPeak()); + counter.registerEvent(1000, start + 500); + Thread.sleep(1500); + assertEquals(4000.0, counter.getPeak()); + Thread.sleep(2000); + assertEquals(4000.0, counter.getPeak()); + counter.registerEvent(1000, start + 500); + assertEquals(4000.0, counter.getPeak()); + Thread.sleep(2000); + counter.registerEvent(1000); + assertEquals(4000.0, counter.getPeak()); + assertEquals(6000, counter.getTotal()); + } + + /** + * Test the current rate is generated correctly. + */ + public void testRate() throws Exception + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + assertEquals(0.0, counter.getRate()); + Thread.sleep(500); + counter.registerEvent(1000); + Thread.sleep(1000); + assertEquals(1000.0, counter.getRate()); + counter.registerEvent(2000); + Thread.sleep(1000); + assertEquals(2000.0, counter.getRate()); + counter.registerEvent(1000); + Thread.sleep(1000); + assertEquals(1000.0, counter.getRate()); + Thread.sleep(1000); + assertEquals(0.0, counter.getRate()); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java index 3ebe631f62..3acd064fd7 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java @@ -122,6 +122,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase } catch (Exception e) { + e.printStackTrace(); fail(e.getMessage()); } } @@ -589,7 +590,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase headerBody.classId = BasicConsumeBodyImpl.CLASS_ID; headerBody.bodySize = 0; - headerBody.properties = properties; + headerBody.setProperties(properties); try { diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java index a75cbe8662..2d41eb9899 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java @@ -102,7 +102,7 @@ public class ReferenceCountingTest extends QpidTestCase ContentHeaderBody chb = new ContentHeaderBody(); BasicContentHeaderProperties bchp = new BasicContentHeaderProperties(); bchp.setDeliveryMode((byte)2); - chb.properties = bchp; + chb.setProperties(bchp); return chb; } diff --git a/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java b/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java index 1ec134e90e..6fbc627d8c 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java +++ b/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.subscription; */ import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -45,6 +46,7 @@ public class MockSubscription implements Subscription private State _state = State.ACTIVE; private ArrayList<QueueEntry> messages = new ArrayList<QueueEntry>(); private final Lock _stateChangeLock = new ReentrantLock(); + private List<QueueEntry> _acceptEntries = null; private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); private final QueueEntry.SubscriptionAssignedState _assignedState = new QueueEntry.SubscriptionAssignedState(this); @@ -54,6 +56,15 @@ public class MockSubscription implements Subscription // Create a simple ID that increments for ever new Subscription private final long _subscriptionID = idGenerator.getAndIncrement(); + public MockSubscription() + { + } + + public MockSubscription(List<QueueEntry> acceptEntries) + { + _acceptEntries = acceptEntries; + } + public void close() { _closed = true; @@ -119,8 +130,15 @@ public class MockSubscription implements Subscription _stateChangeLock.lock(); } - public boolean hasInterest(QueueEntry msg) + public boolean hasInterest(QueueEntry entry) { + if(_acceptEntries != null) + { + //simulate selector behaviour, only signal + //interest in the dictated queue entries + return _acceptEntries.contains(entry); + } + return true; } diff --git a/java/broker/src/test/java/org/apache/qpid/server/subscription/SubscriptionFactoryImplTest.java b/java/broker/src/test/java/org/apache/qpid/server/subscription/SubscriptionFactoryImplTest.java new file mode 100644 index 0000000000..29f45bf7f4 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/subscription/SubscriptionFactoryImplTest.java @@ -0,0 +1,84 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.subscription; + +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.flow.WindowCreditManager; +import org.apache.qpid.server.protocol.ProtocolEngine_0_10; +import org.apache.qpid.server.transport.ServerConnection; +import org.apache.qpid.server.transport.ServerSession; +import org.apache.qpid.server.transport.ServerSessionDelegate; +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.transport.Binary; +import org.apache.qpid.transport.MessageAcceptMode; +import org.apache.qpid.transport.MessageAcquireMode; +import org.apache.qpid.transport.MessageFlowMode; +import org.apache.qpid.transport.TestNetworkConnection; + +public class SubscriptionFactoryImplTest extends InternalBrokerBaseCase +{ + /** + * Tests that while creating Subscriptions of various types, the + * ID numbers assigned are allocated from a common sequence + * (in increasing order). + */ + public void testDifferingSubscriptionTypesShareCommonIdNumberingSequence() throws Exception + { + //create a No-Ack subscription, get the first Subscription ID + long previousId = 0; + Subscription noAckSub = SubscriptionFactoryImpl.INSTANCE.createSubscription(1, getSession(), new AMQShortString("1"), false, null, false, getChannel().getCreditManager()); + previousId = noAckSub.getSubscriptionID(); + + //create an ack subscription, verify the next Subscription ID is used + Subscription ackSub = SubscriptionFactoryImpl.INSTANCE.createSubscription(1, getSession(), new AMQShortString("1"), true, null, false, getChannel().getCreditManager()); + assertEquals("Unexpected Subscription ID allocated", previousId + 1, ackSub.getSubscriptionID()); + previousId = ackSub.getSubscriptionID(); + + //create a browser subscription + FieldTable filters = new FieldTable(); + filters.put(AMQPFilterTypes.NO_CONSUME.getValue(), true); + Subscription browerSub = SubscriptionFactoryImpl.INSTANCE.createSubscription(1, getSession(), new AMQShortString("1"), true, null, false, getChannel().getCreditManager()); + assertEquals("Unexpected Subscription ID allocated", previousId + 1, browerSub.getSubscriptionID()); + previousId = browerSub.getSubscriptionID(); + + //create an BasicGet NoAck subscription + Subscription getNoAckSub = SubscriptionFactoryImpl.INSTANCE.createBasicGetNoAckSubscription(getChannel(), getSession(), new AMQShortString("1"), null, false, + getChannel().getCreditManager(),getChannel().getClientDeliveryMethod(), getChannel().getRecordDeliveryMethod()); + assertEquals("Unexpected Subscription ID allocated", previousId + 1, getNoAckSub.getSubscriptionID()); + previousId = getNoAckSub.getSubscriptionID(); + + //create a 0-10 subscription + ServerConnection conn = new ServerConnection(1); + ProtocolEngine_0_10 engine = new ProtocolEngine_0_10(conn, new TestNetworkConnection(), getRegistry()); + conn.setVirtualHost(getVirtualHost()); + conn.setConnectionConfig(engine); + ServerSessionDelegate sesDel = new ServerSessionDelegate(); + Binary name = new Binary(new byte[]{new Byte("1")}); + ServerSession session = new ServerSession(conn, sesDel, name, 0, engine); + + Subscription sub_0_10 = SubscriptionFactoryImpl.INSTANCE.createSubscription(session, "1", MessageAcceptMode.EXPLICIT, + MessageAcquireMode.PRE_ACQUIRED, MessageFlowMode.WINDOW, new WindowCreditManager(), null, null); + assertEquals("Unexpected Subscription ID allocated", previousId + 1, sub_0_10.getSubscriptionID()); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/subscription/SubscriptionListTest.java b/java/broker/src/test/java/org/apache/qpid/server/subscription/SubscriptionListTest.java new file mode 100644 index 0000000000..c4d1a1e614 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/subscription/SubscriptionListTest.java @@ -0,0 +1,429 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.subscription; + +import org.apache.qpid.server.subscription.SubscriptionList.SubscriptionNode; +import org.apache.qpid.server.subscription.SubscriptionList.SubscriptionNodeIterator; +import org.apache.qpid.test.utils.QpidTestCase; + +public class SubscriptionListTest extends QpidTestCase +{ + private SubscriptionList _subList; + private MockSubscription _sub1; + private MockSubscription _sub2; + private MockSubscription _sub3; + private SubscriptionNode _node; + + protected void setUp() + { + _subList = new SubscriptionList(); + + _sub1 = new MockSubscription(); + _sub2 = new MockSubscription(); + _sub3 = new MockSubscription(); + + _subList.add(_sub1); + _subList.add(_sub2); + _subList.add(_sub3); + + _node = _subList.getHead(); + } + + /** + * Test that if the first (non-head) node in the list is deleted (but is still present), + * it is not returned when searching through the list for the next viable node, and the + * subsequent viable node is returned instead. + */ + public void testFindNextSkipsFirstDeletedNode() + { + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub1).delete()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 2nd subscription", _sub2, _node.getSubscription()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 3rd subscription", _sub3, _node.getSubscription()); + } + + /** + * Test that if a central node in the list is deleted (but is still present), + * it is not returned when searching through the list for the next viable node, + * and the subsequent viable node is returned instead. + */ + public void testFindNextSkipsCentralDeletedNode() + { + assertNotNull("Returned node should not be null", _node = _node.findNext()); + + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub2).delete()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 3rd subscription", _sub3, _node.getSubscription()); + } + + /** + * Test that if the last node in the list is deleted (but is still present), + * it is not returned when searching through the list for the next viable node, + * and null is returned instead. + */ + public void testFindNextSkipsLastDeletedNode() + { + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 1st subscription", _sub1, _node.getSubscription()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 2nd subscription", _sub2, _node.getSubscription()); + + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub3).delete()); + + assertNull("Returned node should be null", _node = _node.findNext()); + } + + /** + * Test that if multiple nodes in the list are deleted (but still present), they + * are not returned when searching through the list for the next viable node, + * and the subsequent viable node is returned instead. + */ + public void testFindNextSkipsMultipleDeletedNode() + { + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub1).delete()); + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub2).delete()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 3rd subscription", _sub3, _node.getSubscription()); + } + + /** + * Test that if a node in the list is marked 'deleted' it is still present in the list + * until actually removed. counter-test to verify above testing of getNext() method. + */ + public void testDeletedNodeStillPresent() + { + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub1).delete()); + + assertNotNull("Node marked deleted should still be present", getNodeForSubscription(_subList, _sub1)); + assertEquals("All 3 nodes are still expected to be present", 3, countNodes(_subList)); + } + + /** + * Traverses the list nodes in a non-mutating fashion, returning the first node which matches the given + * Subscription, or null if none is found. + */ + private SubscriptionNode getNodeForSubscription(final SubscriptionList list, final Subscription sub) + { + SubscriptionNode node = list.getHead(); + while (node != null && node.getSubscription() != sub) + { + node = node.nextNode(); + } + + return node; + } + + /** + * Counts the number of (non-head) nodes in the list. + */ + private int countNodes(final SubscriptionList list) + { + SubscriptionNode node = list.getHead(); + int count; + for(count = -1; node != null; count++) + { + node = node.nextNode(); + } + + return count; + } + + /** + * Tests that the head is returned as expected, and isn't the node for the first subscription. + */ + public void testGetHead() + { + assertNotNull("List head should be non null", _node); + assertNotSame("Head should not be node for first subscription", + _node, getNodeForSubscription(_subList, _sub1)); + } + + /** + * Tests that the size is returned correctly in the face of additions and removals. + */ + public void testGetSize() + { + SubscriptionList subList = new SubscriptionList(); + + assertEquals("Unexpected size result", 0, subList.size()); + + Subscription sub1 = new MockSubscription(); + Subscription sub2 = new MockSubscription(); + Subscription sub3 = new MockSubscription(); + + subList.add(sub1); + assertEquals("Unexpected size result", 1, subList.size()); + + subList.add(sub2); + assertEquals("Unexpected size result", 2, subList.size()); + + subList.add(sub3); + assertEquals("Unexpected size result", 3, subList.size()); + + assertTrue("Removing subscription from list should have succeeded", subList.remove(sub1)); + assertEquals("Unexpected size result", 2, subList.size()); + + assertTrue("Removing subscription from list should have succeeded", subList.remove(sub2)); + assertEquals("Unexpected size result", 1, subList.size()); + + assertTrue("Removing subscription from list should have succeeded", subList.remove(sub3)); + assertEquals("Unexpected size result", 0, subList.size()); + } + + /** + * Test that if the first (non-head) node in the list is removed it is no longer + * present in the node structure of the list at all. + */ + public void testRemoveFirstNode() + { + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub1)); + assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub1)); + assertNull("Should not have been a node present for the removed subscription", getNodeForSubscription(_subList, _sub1)); + assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub2)); + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub3)); + } + + /** + * Test that if a central node in the list is removed it is no longer + * present in the node structure of the list at all. + */ + public void testRemoveCentralNode() + { + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub2)); + assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub2)); + assertNull("Should not have been a node present for the removed subscription", getNodeForSubscription(_subList, _sub2)); + assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub1)); + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub3)); + } + + /** + * Test that if the subscription contained in the last node of the list is removed + * it is no longer present in the node structure of the list at all. However, + * as the last node in the structure can't actually be removed a dummy will instead + * be present. + */ + public void testRemoveLastNode() + { + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub3)); + assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub3)); + assertNull("Should not have been a node present for the removed subscription", getNodeForSubscription(_subList, _sub3)); + + //We actually expect 3 nodes to remain this time, because the last node cant be removed for thread safety reasons, + //however a dummy final node can be used as substitute to allow removal of the subscription node. + assertEquals("Unexpected number of nodes", 2 + 1, countNodes(_subList)); + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub1)); + assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub2)); + } + + /** + * Test that if the subscription not contained in the list is requested to be removed + * that the removal fails + */ + public void testRemoveNonExistantNode() + { + Subscription sub4 = new MockSubscription(); + assertNull("Should not have been a node present for the subscription", getNodeForSubscription(_subList, sub4)); + assertFalse("Removing subscription node should not have succeeded", _subList.remove(sub4)); + assertEquals("Unexpected number of nodes", 3, countNodes(_subList)); + } + + /** + * Test that if a subscription node which occurs later in the main list than the marked node is + * removed from the list after the marked node is also removed, then the marker node doesn't + * serve to retain the subsequent nodes in the list structure (and thus memory) despite their + * removal. + */ + public void testDeletedMarkedNodeDoesntLeakSubsequentlyDeletedNodes() + { + //get the nodes out the list for the 1st and 3rd subscriptions + SubscriptionNode sub1Node = getNodeForSubscription(_subList, _sub1); + assertNotNull("Should have been a node present for the subscription", sub1Node); + SubscriptionNode sub3Node = getNodeForSubscription(_subList, _sub3); + assertNotNull("Should have been a node present for the subscription", sub3Node); + + //mark the first subscription node + assertTrue("should have succeeded in updating the marked node", + _subList.updateMarkedNode(_subList.getMarkedNode(), sub1Node)); + + //remove the 1st subscription from the list + assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub1)); + //verify the 1st subscription is no longer the marker node (replaced by a dummy), or in the main list structure + assertNotSame("Unexpected marker node", sub1Node, _subList.getMarkedNode()); + assertNull("Should not have been a node present in the list structure for the marked-but-removed sub1 node", + getNodeForSubscription(_subList, _sub1)); + + //remove the 2nd subscription from the list + assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub2)); + + //verify the marker node isn't leaking subsequently removed nodes, by ensuring the very next node + //in its list structure is now the 3rd subscription (since the 2nd was removed too) + assertEquals("Unexpected next node", sub3Node, _subList.getMarkedNode().nextNode()); + + //remove the 3rd and final/tail subscription + assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub3)); + + //verify the marker node isn't leaking subsequently removed nodes, by ensuring the very next node + //in its list structure is now the dummy tail (since the 3rd subscription was removed, and a dummy + //tail was inserted) and NOT the 3rd sub node. + assertNotSame("Unexpected next node", sub3Node, _subList.getMarkedNode().nextNode()); + assertTrue("Unexpected next node", _subList.getMarkedNode().nextNode().isDeleted()); + assertNull("Next non-deleted node from the marker should now be the list end, i.e. null", _subList.getMarkedNode().findNext()); + } + + /** + * Test that the marked node 'findNext' behaviour is as expected after a subscription is added + * to the list following the tail subscription node being removed while it is the marked node. + * That is, that the new subscriptions node is returned by getMarkedNode().findNext(). + */ + public void testMarkedNodeFindsNewSubscriptionAfterRemovingTailWhilstMarked() + { + //get the node out the list for the 3rd subscription + SubscriptionNode sub3Node = getNodeForSubscription(_subList, _sub3); + assertNotNull("Should have been a node present for the subscription", sub3Node); + + //mark the 3rd subscription node + assertTrue("should have succeeded in updating the marked node", + _subList.updateMarkedNode(_subList.getMarkedNode(), sub3Node)); + + //verify calling findNext on the marked node returns null, i.e. the end of the list has been reached + assertEquals("Unexpected node after marked node", null, _subList.getMarkedNode().findNext()); + + //remove the 3rd(marked) subscription from the list + assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub3)); + + //add a new 4th subscription to the list + Subscription sub4 = new MockSubscription(); + _subList.add(sub4); + + //get the node out the list for the 4th subscription + SubscriptionNode sub4Node = getNodeForSubscription(_subList, sub4); + assertNotNull("Should have been a node present for the subscription", sub4Node); + + //verify the marked node (which is now a dummy substitute for the 3rd subscription) returns + //the 4th subscriptions node as the next non-deleted node. + assertEquals("Unexpected next node", sub4Node, _subList.getMarkedNode().findNext()); + } + + /** + * Test that setting the marked node to null doesn't cause problems during remove operations + */ + public void testRemoveWithNullMarkedNode() + { + //set the marker to null + assertTrue("should have succeeded in updating the marked node", + _subList.updateMarkedNode(_subList.getMarkedNode(), null)); + + //remove the 1st subscription from the main list + assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub1)); + + //verify the 1st subscription is no longer in the main list structure + assertNull("Should not have been a node present in the main list structure for sub1", + getNodeForSubscription(_subList, _sub1)); + assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); + } + + /** + * Tests that after the first (non-head) node of the list is marked deleted but has not + * yet been removed, the iterator still skips it. + */ + public void testIteratorSkipsFirstDeletedNode() + { + //'delete' but dont remove the node for the 1st subscription + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub1).delete()); + assertNotNull("Should still have been a node present for the deleted subscription", + getNodeForSubscription(_subList, _sub1)); + + SubscriptionNodeIterator iter = _subList.iterator(); + + //verify the iterator returns the 2nd subscriptions node + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected SubscriptionNode", _sub2, iter.getNode().getSubscription()); + + //verify the iterator returns the 3rd subscriptions node and not the 2nd. + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected SubscriptionNode", _sub3, iter.getNode().getSubscription()); + } + + /** + * Tests that after a central node of the list is marked deleted but has not yet been removed, + * the iterator still skips it. + */ + public void testIteratorSkipsCentralDeletedNode() + { + //'delete' but dont remove the node for the 2nd subscription + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub2).delete()); + assertNotNull("Should still have been a node present for the deleted subscription", + getNodeForSubscription(_subList, _sub2)); + + SubscriptionNodeIterator iter = _subList.iterator(); + + //verify the iterator returns the 1st subscriptions node + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected SubscriptionNode", _sub1, iter.getNode().getSubscription()); + + //verify the iterator returns the 3rd subscriptions node and not the 2nd. + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected SubscriptionNode", _sub3, iter.getNode().getSubscription()); + } + + /** + * Tests that after the last node of the list is marked deleted but has not yet been removed, + * the iterator still skips it. + */ + public void testIteratorSkipsDeletedFinalNode() + { + //'delete' but dont remove the node for the 3rd subscription + assertTrue("Deleting subscription node should have succeeded", + getNodeForSubscription(_subList, _sub3).delete()); + assertNotNull("Should still have been a node present for the deleted 3rd subscription", + getNodeForSubscription(_subList, _sub3)); + + SubscriptionNodeIterator iter = _subList.iterator(); + + //verify the iterator returns the 1st subscriptions node + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected SubscriptionNode", _sub1, iter.getNode().getSubscription()); + + //verify the iterator returns the 2nd subscriptions node + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected SubscriptionNode", _sub2, iter.getNode().getSubscription()); + + //verify the iterator can no longer advance and does not return a subscription node + assertFalse("Iterator should not have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected SubscriptionNode", null, iter.getNode()); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/txn/MockAction.java b/java/broker/src/test/java/org/apache/qpid/server/txn/MockAction.java index 975e3e91b9..15c135ea2c 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/txn/MockAction.java +++ b/java/broker/src/test/java/org/apache/qpid/server/txn/MockAction.java @@ -32,13 +32,11 @@ class MockAction implements Action private boolean _rollbackFired = false; private boolean _postCommitFired = false; - @Override public void postCommit() { _postCommitFired = true; } - @Override public void onRollback() { _rollbackFired = true; diff --git a/java/broker/src/test/java/org/apache/qpid/server/txn/MockServerMessage.java b/java/broker/src/test/java/org/apache/qpid/server/txn/MockServerMessage.java index 64c62fd029..422105e410 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/txn/MockServerMessage.java +++ b/java/broker/src/test/java/org/apache/qpid/server/txn/MockServerMessage.java @@ -46,67 +46,56 @@ class MockServerMessage implements ServerMessage this.persistent = persistent; } - @Override public boolean isPersistent() { return persistent; } - @Override public MessageReference newReference() { throw new NotImplementedException(); } - @Override public boolean isImmediate() { throw new NotImplementedException(); } - @Override public long getSize() { throw new NotImplementedException(); } - @Override public SessionConfig getSessionConfig() { throw new NotImplementedException(); } - @Override public String getRoutingKey() { throw new NotImplementedException(); } - @Override public AMQMessageHeader getMessageHeader() { throw new NotImplementedException(); } - @Override public long getExpiration() { throw new NotImplementedException(); } - @Override public int getContent(ByteBuffer buf, int offset) { throw new NotImplementedException(); } - @Override public long getArrivalTime() { throw new NotImplementedException(); } - @Override public Long getMessageNumber() { return 0L; diff --git a/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java b/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java index 5700bba9f8..ff372532ac 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java +++ b/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java @@ -61,7 +61,6 @@ class MockStoreTransaction implements Transaction return _state; } - @Override public void enqueueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException { if (_throwExceptionOnQueueOp) @@ -83,8 +82,6 @@ class MockStoreTransaction implements Transaction return _numberOfEnqueuedMessages; } - - @Override public void dequeueMessage(TransactionLogResource queue, Long messageId) throws AMQStoreException { if (_throwExceptionOnQueueOp) @@ -95,19 +92,16 @@ class MockStoreTransaction implements Transaction _numberOfDequeuedMessages++; } - @Override public void commitTran() throws AMQStoreException { _state = TransactionState.COMMITTED; } - @Override public StoreFuture commitTranAsync() throws AMQStoreException { throw new NotImplementedException(); } - @Override public void abortTran() throws AMQStoreException { _state = TransactionState.ABORTED; @@ -117,14 +111,11 @@ class MockStoreTransaction implements Transaction { return new TransactionLog() { - - @Override public void configureTransactionLog(String name, TransactionLogRecoveryHandler recoveryHandler, Configuration storeConfiguration, LogSubject logSubject) throws Exception { } - - @Override + public Transaction newTransaction() { storeTransaction.setState(TransactionState.STARTED); diff --git a/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java b/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java index 925b161118..a97134a58d 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java +++ b/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java @@ -44,14 +44,13 @@ import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; -import org.apache.qpid.util.MockChannel; public class InternalBrokerBaseCase extends QpidTestCase { private IApplicationRegistry _registry; private MessageStore _messageStore; - private MockChannel _channel; + private AMQChannel _channel; private InternalTestProtocolSession _session; private VirtualHost _virtualHost; private AMQQueue _queue; @@ -111,7 +110,7 @@ public class InternalBrokerBaseCase extends QpidTestCase _session = new InternalTestProtocolSession(_virtualHost); CurrentActor.set(_session.getLogActor()); - _channel = new MockChannel(_session, 1, _messageStore); + _channel = new AMQChannel(_session, 1, _messageStore); _session.addChannel(_channel); } @@ -243,7 +242,7 @@ public class InternalBrokerBaseCase extends QpidTestCase //Make Message Persistent properties.setDeliveryMode((byte) 2); - _headerBody.properties = properties; + _headerBody.setProperties(properties); channel.publishContentHeader(_headerBody); } @@ -283,12 +282,12 @@ public class InternalBrokerBaseCase extends QpidTestCase _messageStore = messageStore; } - public MockChannel getChannel() + public AMQChannel getChannel() { return _channel; } - public void setChannel(MockChannel channel) + public void setChannel(AMQChannel channel) { _channel = channel; } diff --git a/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java index af8997cf40..3c6857e8a9 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java +++ b/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java @@ -20,27 +20,72 @@ */ package org.apache.qpid.server.util; +import java.util.Properties; + import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.logging.NullRootMessageLogger; +import org.apache.qpid.server.logging.actors.BrokerActor; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.GenericActor; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabaseManager; - -import java.util.Properties; - +import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabase; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; public class TestApplicationRegistry extends ApplicationRegistry { + public TestApplicationRegistry(ServerConfiguration config) throws ConfigurationException { super(config); } - protected void createDatabaseManager(ServerConfiguration configuration) throws Exception + @Override + public void initialise() throws Exception + { + CurrentActor.setDefault(new BrokerActor(new NullRootMessageLogger())); + GenericActor.setDefaultMessageLogger(new NullRootMessageLogger()); + super.initialise(); + } + + /** + * @see org.apache.qpid.server.registry.ApplicationRegistry#createAuthenticationManager() + */ + @Override + protected AuthenticationManager createAuthenticationManager() throws ConfigurationException { - Properties users = new Properties(); + final Properties users = new Properties(); users.put("guest","guest"); users.put("admin","admin"); - _databaseManager = new PropertiesPrincipalDatabaseManager("testPasswordFile", users); + + final PropertiesPrincipalDatabase ppd = new PropertiesPrincipalDatabase(users); + + AuthenticationManager pdam = new PrincipalDatabaseAuthenticationManager() + { + + /** + * @see org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager#configure(org.apache.qpid.server.configuration.plugins.ConfigurationPlugin) + */ + @Override + public void configure(ConfigurationPlugin config) throws ConfigurationException + { + // We don't pass configuration to this test instance. + } + + @Override + public void initialise() + { + setPrincipalDatabase(ppd); + + super.initialise(); + } + }; + + pdam.initialise(); + + return pdam; } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/HouseKeepingTaskTest.java b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/HouseKeepingTaskTest.java new file mode 100644 index 0000000000..98bf381712 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/HouseKeepingTaskTest.java @@ -0,0 +1,67 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.virtualhost; + +import java.util.concurrent.CountDownLatch; + +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.NullRootMessageLogger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.TestLogActor; +import org.apache.qpid.test.utils.QpidTestCase; + +public class HouseKeepingTaskTest extends QpidTestCase +{ + /** + * Tests that the abstract HouseKeepingTask properly cleans up any LogActor + * it adds to the CurrentActor stack by verifying the CurrentActor set + * before task execution is the CurrentActor after execution. + */ + public void testCurrentActorStackBalance() throws Exception + { + //create and set a test actor + LogActor testActor = new TestLogActor(new NullRootMessageLogger()); + CurrentActor.set(testActor); + + //verify it is returned correctly before executing a HouseKeepingTask + assertEquals("Expected LogActor was not returned", testActor, CurrentActor.get()); + + final CountDownLatch latch = new CountDownLatch(1); + HouseKeepingTask testTask = new HouseKeepingTask(new MockVirtualHost("HouseKeepingTaskTestVhost")) + { + @Override + public void execute() + { + latch.countDown(); + } + }; + + //run the test HouseKeepingTask using the current Thread to influence its CurrentActor stack + testTask.run(); + + assertEquals("The expected LogActor was not returned, the CurrentActor stack has become unbalanced", + testActor, CurrentActor.get()); + assertEquals("HouseKeepingTask execute() method was not run", 0, latch.getCount()); + + //clean up the test actor + CurrentActor.remove(); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java new file mode 100644 index 0000000000..7aa314bf22 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java @@ -0,0 +1,271 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.virtualhost; + +import java.util.UUID; + +import org.apache.qpid.server.binding.BindingFactory; +import org.apache.qpid.server.configuration.BrokerConfig; +import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.configuration.ConfiguredObject; +import org.apache.qpid.server.configuration.VirtualHostConfig; +import org.apache.qpid.server.configuration.VirtualHostConfigType; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.connection.IConnectionRegistry; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.federation.BrokerLink; +import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.queue.QueueRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.stats.StatisticsCounter; +import org.apache.qpid.server.store.DurableConfigurationStore; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.TransactionLog; + +public class MockVirtualHost implements VirtualHost +{ + private String _name; + + public MockVirtualHost(String name) + { + _name = name; + } + + public void close() + { + + } + + public void createBrokerConnection(String transport, String host, int port, + String vhost, boolean durable, String authMechanism, + String username, String password) + { + + } + + public IApplicationRegistry getApplicationRegistry() + { + return null; + } + + public AuthenticationManager getAuthenticationManager() + { + return null; + } + + public BindingFactory getBindingFactory() + { + return null; + } + + public UUID getBrokerId() + { + return null; + } + + public ConfigStore getConfigStore() + { + return null; + } + + public VirtualHostConfiguration getConfiguration() + { + return null; + } + + public IConnectionRegistry getConnectionRegistry() + { + return null; + } + + public DurableConfigurationStore getDurableConfigurationStore() + { + return null; + } + + public ExchangeFactory getExchangeFactory() + { + return null; + } + + public ExchangeRegistry getExchangeRegistry() + { + return null; + } + + public int getHouseKeepingActiveCount() + { + return 0; + } + + public long getHouseKeepingCompletedTaskCount() + { + return 0; + } + + public int getHouseKeepingPoolSize() + { + return 0; + } + + public long getHouseKeepingTaskCount() + { + return 0; + } + + public ManagedObject getManagedObject() + { + return null; + } + + public MessageStore getMessageStore() + { + return null; + } + + public String getName() + { + return _name; + } + + public QueueRegistry getQueueRegistry() + { + return null; + } + + public SecurityManager getSecurityManager() + { + return null; + } + + public TransactionLog getTransactionLog() + { + return null; + } + + public void removeBrokerConnection(BrokerLink brokerLink) + { + + } + + public void scheduleHouseKeepingTask(long period, HouseKeepingTask task) + { + + } + + public void setHouseKeepingPoolSize(int newSize) + { + + } + + public BrokerConfig getBroker() + { + return null; + } + + public String getFederationTag() + { + return null; + } + + public void setBroker(BrokerConfig brokerConfig) + { + + } + + public VirtualHostConfigType getConfigType() + { + return null; + } + + public long getCreateTime() + { + return 0; + } + + public UUID getId() + { + return null; + } + + public ConfiguredObject<VirtualHostConfigType, VirtualHostConfig> getParent() + { + return null; + } + + public boolean isDurable() + { + return false; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return null; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return null; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return null; + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return null; + } + + public void initialiseStatistics() + { + + } + + public boolean isStatisticsEnabled() + { + return false; + } + + public void registerMessageDelivered(long messageSize) + { + + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + + } + + public void resetStatistics() + { + + } + + public void setStatisticsEnabled(boolean enabled) + { + + } +}
\ No newline at end of file diff --git a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java new file mode 100644 index 0000000000..c87e5a1648 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java @@ -0,0 +1,214 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.virtualhost; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.server.configuration.ServerConfiguration; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.util.TestApplicationRegistry; +import org.apache.qpid.test.utils.QpidTestCase; + +public class VirtualHostImplTest extends QpidTestCase +{ + private ServerConfiguration _configuration; + private ApplicationRegistry _registry; + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + ApplicationRegistry.remove(); + } + + /** + * Tests that custom routing keys for the queue specified in the configuration + * file are correctly bound to the exchange (in addition to the queue name) + */ + public void testSpecifyingCustomBindings() throws Exception + { + customBindingTestImpl(new String[]{"custom1","custom2"}); + } + + /** + * Tests that a queue specified in the configuration file to be bound to a + * specified(non-default) direct exchange is a correctly bound to the exchange + * and the default exchange using the queue name. + */ + public void testQueueSpecifiedInConfigurationIsBoundToDefaultExchange() throws Exception + { + customBindingTestImpl(new String[0]); + } + + private void customBindingTestImpl(final String[] routingKeys) throws Exception + { + String exchangeName = getName() +".direct"; + String vhostName = getName(); + String queueName = getName(); + + File config = writeConfigFile(vhostName, queueName, exchangeName, false, routingKeys); + VirtualHost vhost = createVirtualHost(vhostName, config); + assertNotNull("virtualhost should exist", vhost); + + AMQQueue queue = vhost.getQueueRegistry().getQueue(queueName); + assertNotNull("queue should exist", queue); + + Exchange defaultExch = vhost.getExchangeRegistry().getDefaultExchange(); + assertTrue("queue should have been bound to default exchange with its name", defaultExch.isBound(queueName, queue)); + + Exchange exch = vhost.getExchangeRegistry().getExchange(exchangeName); + assertTrue("queue should have been bound to " + exchangeName + " with its name", exch.isBound(queueName, queue)); + + for(String key: routingKeys) + { + assertTrue("queue should have been bound to " + exchangeName + " with key " + key, exch.isBound(key, queue)); + } + } + + /** + * Tests that specifying custom routing keys for a queue in the configuration file results in failure + * to create the vhost (since this is illegal, only queue names are used with the default exchange) + */ + public void testSpecifyingCustomBindingForDefaultExchangeThrowsException() throws Exception + { + File config = writeConfigFile(getName(), getName(), null, false, new String[]{"custom-binding"}); + + try + { + createVirtualHost(getName(), config); + fail("virtualhost creation should have failed due to illegal configuration"); + } + catch (ConfigurationException e) + { + //expected + } + } + + /** + * Tests that specifying an unknown exchange to bind the queue to results in failure to create the vhost + */ + public void testSpecifyingUnknownExchangeThrowsException() throws Exception + { + File config = writeConfigFile(getName(), getName(), "made-up-exchange", true, new String[0]); + + try + { + createVirtualHost(getName(), config); + fail("virtualhost creation should have failed due to illegal configuration"); + } + catch (ConfigurationException e) + { + //expected + } + } + + private VirtualHost createVirtualHost(String vhostName, File config) throws Exception + { + _configuration = new ServerConfiguration(new XMLConfiguration(config)); + + _registry = new TestApplicationRegistry(_configuration); + ApplicationRegistry.initialise(_registry); + + return _registry.getVirtualHostRegistry().getVirtualHost(vhostName); + } + + /** + * Create a configuration file for testing virtualhost creation + * + * @param vhostName name of the virtualhost + * @param queueName name of the queue + * @param exchangeName name of a direct exchange to declare (unless dontDeclare = true) and bind the queue to (null = none) + * @param dontDeclare if true then dont declare the exchange, even if its name is non-null + * @param routingKeys routingKeys to bind the queue with (empty array = none) + * @return + */ + private File writeConfigFile(String vhostName, String queueName, String exchangeName, boolean dontDeclare, String[] routingKeys) + { + File tmpFile = null; + try + { + tmpFile = File.createTempFile(getName(), ".tmp"); + tmpFile.deleteOnExit(); + + FileWriter fstream = new FileWriter(tmpFile); + BufferedWriter writer = new BufferedWriter(fstream); + + //extra outer tag to please Commons Configuration + writer.write("<configuration>"); + + writer.write("<virtualhosts>"); + writer.write(" <default>" + vhostName + "</default>"); + writer.write(" <virtualhost>"); + writer.write(" <store>"); + writer.write(" <class>" + TestableMemoryMessageStore.class.getName() + "</class>"); + writer.write(" </store>"); + writer.write(" <name>" + vhostName + "</name>"); + writer.write(" <" + vhostName + ">"); + if(exchangeName != null && !dontDeclare) + { + writer.write(" <exchanges>"); + writer.write(" <exchange>"); + writer.write(" <type>direct</type>"); + writer.write(" <name>" + exchangeName + "</name>"); + writer.write(" </exchange>"); + writer.write(" </exchanges>"); + } + writer.write(" <queues>"); + writer.write(" <queue>"); + writer.write(" <name>" + queueName + "</name>"); + writer.write(" <" + queueName + ">"); + if(exchangeName != null) + { + writer.write(" <exchange>" + exchangeName + "</exchange>"); + } + for(String routingKey: routingKeys) + { + writer.write(" <routingKey>" + routingKey + "</routingKey>"); + } + writer.write(" </" + queueName + ">"); + writer.write(" </queue>"); + writer.write(" </queues>"); + writer.write(" </" + vhostName + ">"); + writer.write(" </virtualhost>"); + writer.write("</virtualhosts>"); + + writer.write("</configuration>"); + + writer.flush(); + writer.close(); + } + catch (IOException e) + { + fail("Unable to create virtualhost configuration"); + } + + return tmpFile; + } +} diff --git a/java/broker/src/velocity/java/org/apache/qpid/server/logging/GenerateLogMessages.java b/java/broker/src/velocity/java/org/apache/qpid/server/logging/GenerateLogMessages.java index 902b86f80b..a39799a6b6 100644 --- a/java/broker/src/velocity/java/org/apache/qpid/server/logging/GenerateLogMessages.java +++ b/java/broker/src/velocity/java/org/apache/qpid/server/logging/GenerateLogMessages.java @@ -481,7 +481,7 @@ public class GenerateLogMessages // Only check the text inside the braces '{}' int typeIndexEnd = parametersString[index].indexOf("}", typeIndex); String typeString = parametersString[index].substring(typeIndex, typeIndexEnd); - if (typeString.contains("number")) + if (typeString.contains("number") || typeString.contains("choice")) { type = "Number"; } |