diff options
| author | Keith Wall <kwall@apache.org> | 2014-03-25 17:54:10 +0000 |
|---|---|---|
| committer | Keith Wall <kwall@apache.org> | 2014-03-25 17:54:10 +0000 |
| commit | cd6130384dc5f27ad494eabf8a2b15ca79280aa1 (patch) | |
| tree | 77d7b1f0ced2cea6b031327fcb5c8143d763cf9d /qpid/java | |
| parent | fcc3f654b60b7dd2180afe73e8809545725b41af (diff) | |
| parent | 809061e0024b74f89afdeff8ba83d6514589f417 (diff) | |
| download | qpid-python-cd6130384dc5f27ad494eabf8a2b15ca79280aa1.tar.gz | |
NO-JIRA: Merge changes from trunk.
Command was:
svn merge https://svn.apache.org/repos/asf/qpid/trunk
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-broker-bdb-ha2@1581428 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java')
110 files changed, 4916 insertions, 1492 deletions
diff --git a/qpid/java/bdbstore/pom.xml b/qpid/java/bdbstore/pom.xml index d7d12f0b33..5d34559a46 100644 --- a/qpid/java/bdbstore/pom.xml +++ b/qpid/java/bdbstore/pom.xml @@ -102,6 +102,20 @@ </includes> </resource> </resources> + + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> </build> </project> diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBConfiguredObjectRecord.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBConfiguredObjectRecord.java new file mode 100644 index 0000000000..f13e4dd08b --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBConfiguredObjectRecord.java @@ -0,0 +1,107 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb; + +import org.apache.qpid.server.store.ConfiguredObjectRecord; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class BDBConfiguredObjectRecord implements ConfiguredObjectRecord +{ + private final UUID _id; + private final String _type; + private final Map<String,Object> _attributes; + private Map<String, ConfiguredObjectRecord> _parents = new HashMap<String, ConfiguredObjectRecord>(); + + public BDBConfiguredObjectRecord(final UUID id, final String type, final Map<String, Object> attributes) + { + _id = id; + _type = type; + _attributes = Collections.unmodifiableMap(attributes); + } + + public UUID getId() + { + return _id; + } + + public String getType() + { + return _type; + } + + public Map<String, Object> getAttributes() + { + return _attributes; + } + + void addParent(String parentType, ConfiguredObjectRecord parent) + { + _parents.put(parentType, parent); + } + + @Override + public Map<String, ConfiguredObjectRecord> getParents() + { + return Collections.unmodifiableMap(_parents); + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final BDBConfiguredObjectRecord that = (BDBConfiguredObjectRecord) o; + + if (_attributes != null ? !_attributes.equals(that._attributes) : that._attributes != null) + { + return false; + } + if (_id != null ? !_id.equals(that._id) : that._id != null) + { + return false; + } + if (_type != null ? !_type.equals(that._type) : that._type != null) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result = _id != null ? _id.hashCode() : 0; + result = 31 * result + (_type != null ? _type.hashCode() : 0); + return result; + } +} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java index 7e42d09ba6..492ec9d7bf 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBHAVirtualHost.java @@ -242,10 +242,10 @@ public class BDBHAVirtualHost extends AbstractVirtualHost DurableConfigurationRecoverer configRecoverer = new DurableConfigurationRecoverer(getName(), getDurableConfigurationRecoverers(), new DefaultUpgraderProvider(BDBHAVirtualHost.this, getExchangeRegistry()), getEventLogger()); - _messageStore.recoverConfigurationStore(configRecoverer); + _messageStore.recoverConfigurationStore(getModel(), configRecoverer); VirtualHostConfigRecoveryHandler recoveryHandler = new VirtualHostConfigRecoveryHandler(BDBHAVirtualHost.this); - _messageStore.recoverMessageStore(recoveryHandler, recoveryHandler); + _messageStore.recoverMessageStore(getModel(), recoveryHandler, recoveryHandler); } catch (Exception e) { diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java index 2022f36bd9..ec6ae23367 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStore.java @@ -25,6 +25,8 @@ import java.lang.ref.SoftReference; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -34,6 +36,7 @@ import java.util.concurrent.atomic.AtomicLong; import org.apache.log4j.Logger; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.store.ConfigurationRecoveryHandler; import org.apache.qpid.server.store.ConfiguredObjectRecord; @@ -54,11 +57,13 @@ import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.store.TransactionLogRecoveryHandler; import org.apache.qpid.server.store.TransactionLogRecoveryHandler.QueueEntryRecoveryHandler; import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.store.berkeleydb.entry.HierarchyKey; import org.apache.qpid.server.store.berkeleydb.entry.PreparedTransaction; import org.apache.qpid.server.store.berkeleydb.entry.QueueEntryKey; import org.apache.qpid.server.store.berkeleydb.entry.Xid; import org.apache.qpid.server.store.berkeleydb.tuple.ConfiguredObjectBinding; import org.apache.qpid.server.store.berkeleydb.tuple.ContentBinding; +import org.apache.qpid.server.store.berkeleydb.tuple.HierarchyKeyBinding; import org.apache.qpid.server.store.berkeleydb.tuple.MessageMetaDataBinding; import org.apache.qpid.server.store.berkeleydb.tuple.PreparedTransactionBinding; import org.apache.qpid.server.store.berkeleydb.tuple.QueueEntryBinding; @@ -94,9 +99,11 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore { private static final Logger LOGGER = Logger.getLogger(BDBMessageStore.class); - public static final int VERSION = 7; + public static final int VERSION = 8; private static final int LOCK_RETRY_ATTEMPTS = 5; private static String CONFIGURED_OBJECTS_DB_NAME = "CONFIGURED_OBJECTS"; + private static String CONFIGURED_OBJECT_HIERARCHY_DB_NAME = "CONFIGURED_OBJECT_HIERARCHY"; + private static String MESSAGE_META_DATA_DB_NAME = "MESSAGE_METADATA"; private static String MESSAGE_CONTENT_DB_NAME = "MESSAGE_CONTENT"; private static String DELIVERY_DB_NAME = "QUEUE_ENTRIES"; @@ -106,7 +113,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore private static String LINKDB_NAME = "LINKS"; private static String XID_DB_NAME = "XIDS"; private static String CONFIG_VERSION_DB_NAME = "CONFIG_VERSION"; - private static final String[] CONFIGURATION_STORE_DATABASE_NAMES = new String[] { CONFIGURED_OBJECTS_DB_NAME, CONFIG_VERSION_DB_NAME }; + private static final String[] CONFIGURATION_STORE_DATABASE_NAMES = new String[] { CONFIGURED_OBJECTS_DB_NAME, CONFIG_VERSION_DB_NAME , CONFIGURED_OBJECT_HIERARCHY_DB_NAME}; private static final String[] MESSAGE_STORE_DATABASE_NAMES = new String[] { MESSAGE_META_DATA_DB_NAME, MESSAGE_CONTENT_DB_NAME, DELIVERY_DB_NAME, BRIDGEDB_NAME, LINKDB_NAME, XID_DB_NAME }; private EnvironmentFacade _environmentFacade; @@ -163,7 +170,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore } @Override - public void recoverConfigurationStore(ConfigurationRecoveryHandler recoveryHandler) + public void recoverConfigurationStore(ConfiguredObject<?> parent, ConfigurationRecoveryHandler recoveryHandler) { _configurationStoreStateManager.attainState(State.ACTIVATING); @@ -172,7 +179,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore dbConfig.setAllowCreate(true); try { - new Upgrader(_environmentFacade.getEnvironment(), _virtualHostName).upgradeIfNecessary(); + new Upgrader(_environmentFacade.getEnvironment(), parent).upgradeIfNecessary(); _environmentFacade.openDatabases(dbConfig, CONFIGURATION_STORE_DATABASE_NAMES); } catch(DatabaseException e) @@ -216,7 +223,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore } @Override - public synchronized void recoverMessageStore(MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) throws StoreException + public synchronized void recoverMessageStore(ConfiguredObject<?> parent, MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) throws StoreException { _messageStoreStateManager.attainState(State.ACTIVATING); DatabaseConfig dbConfig = new DatabaseConfig(); @@ -224,13 +231,13 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore dbConfig.setAllowCreate(true); try { - new Upgrader(_environmentFacade.getEnvironment(), _virtualHostName).upgradeIfNecessary(); + new Upgrader(_environmentFacade.getEnvironment(), parent).upgradeIfNecessary(); _environmentFacade.openDatabases(dbConfig, MESSAGE_STORE_DATABASE_NAMES); _totalStoreSize = getSizeOnDisk(); } catch(DatabaseException e) { - throw _environmentFacade.handleDatabaseException("Cannot activate message store", e); + throw _environmentFacade.handleDatabaseException("Cannot upgrade message store or open datatbases", e); } if(messageRecoveryHandler != null) @@ -352,10 +359,11 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore @SuppressWarnings("resource") private void updateConfigVersion(int newConfigVersion) throws StoreException { + Transaction txn = null; Cursor cursor = null; try { - Transaction txn = _environmentFacade.getEnvironment().beginTransaction(null, null); + txn = _environmentFacade.getEnvironment().beginTransaction(null, null); cursor = getConfigVersionDb().openCursor(txn, null); DatabaseEntry key = new DatabaseEntry(); ByteBinding.byteToEntry((byte) 0,key); @@ -373,10 +381,12 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore cursor.close(); cursor = null; txn.commit(); + txn = null; } finally { closeCursorSafely(cursor); + abortTransactionIgnoringException("Error setting config version", txn);; } } @@ -412,24 +422,57 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore private void loadConfiguredObjects(ConfigurationRecoveryHandler crh) throws DatabaseException, StoreException { - Cursor cursor = null; + Cursor objectsCursor = null; + Cursor hierarchyCursor = null; try { - cursor = getConfiguredObjectsDb().openCursor(null, null); + objectsCursor = getConfiguredObjectsDb().openCursor(null, null); DatabaseEntry key = new DatabaseEntry(); DatabaseEntry value = new DatabaseEntry(); - while (cursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS) + + Map<UUID, BDBConfiguredObjectRecord> configuredObjects = + new HashMap<UUID, BDBConfiguredObjectRecord>(); + + while (objectsCursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS) { UUID id = UUIDTupleBinding.getInstance().entryToObject(key); - ConfiguredObjectRecord configuredObject = new ConfiguredObjectBinding(id).entryToObject(value); - crh.configuredObject(configuredObject.getId(),configuredObject.getType(),configuredObject.getAttributes()); + BDBConfiguredObjectRecord configuredObject = + (BDBConfiguredObjectRecord) new ConfiguredObjectBinding(id).entryToObject(value); + configuredObjects.put(configuredObject.getId(), configuredObject); + } + + // set parents + hierarchyCursor = getConfiguredObjectHierarchyDb().openCursor(null, null); + while (hierarchyCursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS) + { + HierarchyKey hk = HierarchyKeyBinding.getInstance().entryToObject(key); + UUID parentId = UUIDTupleBinding.getInstance().entryToObject(value); + BDBConfiguredObjectRecord child = configuredObjects.get(hk.getChildId()); + if(child != null) + { + ConfiguredObjectRecord parent = configuredObjects.get(parentId); + if(parent != null) + { + child.addParent(hk.getParentType(), parent); + } + else if(hk.getParentType().equals("Exchange")) + { + // TODO - remove this hack for the pre-defined exchanges + child.addParent(hk.getParentType(), new BDBConfiguredObjectRecord(parentId, "Exchange", Collections.<String,Object>emptyMap())); + } + } } + for (ConfiguredObjectRecord record : configuredObjects.values()) + { + crh.configuredObject(record); + } } finally { - closeCursorSafely(cursor); + closeCursorSafely(objectsCursor); + closeCursorSafely(hierarchyCursor); } } @@ -448,7 +491,6 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore } } - private void recoverMessages(MessageStoreRecoveryHandler msrh) throws StoreException { StoredMessageRecoveryHandler mrh = msrh.begin(); @@ -568,9 +610,8 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore dtxrh.completeDtxRecordRecovery(); } - public void removeMessage(long messageId, boolean sync) throws StoreException + void removeMessage(long messageId, boolean sync) throws StoreException { - boolean complete = false; com.sleepycat.je.Transaction tx = null; @@ -715,116 +756,130 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore } @Override - public void create(UUID id, String type, Map<String, Object> attributes) throws StoreException + public void create(ConfiguredObjectRecord configuredObject) throws StoreException { if (_configurationStoreStateManager.isInState(State.ACTIVE)) { - ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(id, type, attributes); - storeConfiguredObjectEntry(configuredObject); + com.sleepycat.je.Transaction txn = null; + try + { + txn = _environmentFacade.getEnvironment().beginTransaction(null, null); + storeConfiguredObjectEntry(txn, configuredObject); + txn.commit(); + txn = null; + } + catch (DatabaseException e) + { + throw _environmentFacade.handleDatabaseException("Error creating configured object " + configuredObject + + " in database: " + e.getMessage(), e); + } + finally + { + if (txn != null) + { + abortTransactionIgnoringException("Error creating configured object", txn); + } + } } } @Override - public void remove(UUID id, String type) throws StoreException + public UUID[] remove(final ConfiguredObjectRecord... objects) throws StoreException { - if (LOGGER.isDebugEnabled()) + com.sleepycat.je.Transaction txn = null; + try { - LOGGER.debug("public void remove(id = " + id + ", type="+type+"): called"); + txn = _environmentFacade.getEnvironment().beginTransaction(null, null); + + Collection<UUID> removed = new ArrayList<UUID>(objects.length); + for(ConfiguredObjectRecord record : objects) + { + if(removeConfiguredObject(txn, record) == OperationStatus.SUCCESS) + { + removed.add(record.getId()); + } + } + + txn.commit(); + txn = null; + return removed.toArray(new UUID[removed.size()]); } - OperationStatus status = removeConfiguredObject(null, id); - if (status == OperationStatus.NOTFOUND) + catch (DatabaseException e) { - throw new StoreException("Configured object of type " + type + " with id " + id + " not found"); + throw _environmentFacade.handleDatabaseException("Error deleting configured objects from database", e); } - } - - @Override - public UUID[] removeConfiguredObjects(final UUID... objects) throws StoreException - { - com.sleepycat.je.Transaction txn = _environmentFacade.getEnvironment().beginTransaction(null, null); - Collection<UUID> removed = new ArrayList<UUID>(objects.length); - for(UUID id : objects) + finally { - if(removeConfiguredObject(txn, id) == OperationStatus.SUCCESS) + if (txn != null) { - removed.add(id); + abortTransactionIgnoringException("Error deleting configured objects", txn); } } - commitTransaction(txn); - return removed.toArray(new UUID[removed.size()]); + } - private void commitTransaction(com.sleepycat.je.Transaction txn) throws StoreException + @Override + public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException { + com.sleepycat.je.Transaction txn = null; try { + txn = _environmentFacade.getEnvironment().beginTransaction(null, null); + for(ConfiguredObjectRecord record : records) + { + update(createIfNecessary, record, txn); + } txn.commit(); + txn = null; } - catch(DatabaseException e) + catch (DatabaseException e) { - throw _environmentFacade.handleDatabaseException("Cannot commit transaction on configured objects removal", e); + throw _environmentFacade.handleDatabaseException("Error updating configuration details within the store: " + e,e); } - } - - @Override - public void update(UUID id, String type, Map<String, Object> attributes) throws StoreException - { - update(false, id, type, attributes, null); - } - - @Override - public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException - { - com.sleepycat.je.Transaction txn = _environmentFacade.getEnvironment().beginTransaction(null, null); - for(ConfiguredObjectRecord record : records) + finally { - update(createIfNecessary, record.getId(), record.getType(), record.getAttributes(), txn); + if (txn != null) + { + abortTransactionIgnoringException("Error updating configuration details within the store", txn); + } } - commitTransaction(txn); + } - private void update(boolean createIfNecessary, UUID id, String type, Map<String, Object> attributes, com.sleepycat.je.Transaction txn) throws StoreException + private void update(boolean createIfNecessary, ConfiguredObjectRecord record, com.sleepycat.je.Transaction txn) throws StoreException { if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Updating " + type + ", id: " + id); + LOGGER.debug("Updating " + record.getType() + ", id: " + record.getId()); } - try - { - DatabaseEntry key = new DatabaseEntry(); - UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance(); - keyBinding.objectToEntry(id, key); + DatabaseEntry key = new DatabaseEntry(); + UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance(); + keyBinding.objectToEntry(record.getId(), key); - DatabaseEntry value = new DatabaseEntry(); - DatabaseEntry newValue = new DatabaseEntry(); - ConfiguredObjectBinding configuredObjectBinding = ConfiguredObjectBinding.getInstance(); + DatabaseEntry value = new DatabaseEntry(); + DatabaseEntry newValue = new DatabaseEntry(); + ConfiguredObjectBinding configuredObjectBinding = ConfiguredObjectBinding.getInstance(); - OperationStatus status = getConfiguredObjectsDb().get(txn, key, value, LockMode.DEFAULT); - if (status == OperationStatus.SUCCESS || (createIfNecessary && status == OperationStatus.NOTFOUND)) + OperationStatus status = getConfiguredObjectsDb().get(txn, key, value, LockMode.DEFAULT); + final boolean isNewRecord = status == OperationStatus.NOTFOUND; + if (status == OperationStatus.SUCCESS || (createIfNecessary && isNewRecord)) + { + // write the updated entry to the store + configuredObjectBinding.objectToEntry(record, newValue); + status = getConfiguredObjectsDb().put(txn, key, newValue); + if (status != OperationStatus.SUCCESS) { - ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(id, type, attributes); - - // write the updated entry to the store - configuredObjectBinding.objectToEntry(newQueueRecord, newValue); - status = getConfiguredObjectsDb().put(txn, key, newValue); - if (status != OperationStatus.SUCCESS) - { - throw new StoreException("Error updating configuration details within the store: " + status); - } + throw new StoreException("Error updating configuration details within the store: " + status); } - else if (status != OperationStatus.NOTFOUND) + if(isNewRecord) { - throw new StoreException("Error finding configuration details within the store: " + status); + writeHierarchyRecords(txn, record); } } - catch (DatabaseException e) + else if (status != OperationStatus.NOTFOUND) { - if (txn != null) - { - abortTransactionIgnoringException("Error updating configuration details within the store: " + e.getMessage(), txn); - } - throw _environmentFacade.handleDatabaseException("Error updating configuration details within the store: " + e,e); + throw new StoreException("Error finding configuration details within the store: " + status); } } @@ -1018,7 +1073,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore * * @throws StoreException If the operation fails for any reason. */ - public void abortTran(final com.sleepycat.je.Transaction tx) throws StoreException + private void abortTran(final com.sleepycat.je.Transaction tx) throws StoreException { if (LOGGER.isDebugEnabled()) { @@ -1091,7 +1146,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore * * @return A fresh message id. */ - public long getNewMessageId() + private long getNewMessageId() { return _messageId.incrementAndGet(); } @@ -1106,7 +1161,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore * * @throws StoreException If the operation fails for any reason, or if the specified message does not exist. */ - protected void addContent(final com.sleepycat.je.Transaction tx, long messageId, int offset, + private void addContent(final com.sleepycat.je.Transaction tx, long messageId, int offset, ByteBuffer contentBody) throws StoreException { DatabaseEntry key = new DatabaseEntry(); @@ -1183,7 +1238,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore * * @throws StoreException If the operation fails for any reason, or if the specified message does not exist. */ - public StorableMessageMetaData getMessageMetaData(long messageId) throws StoreException + StorableMessageMetaData getMessageMetaData(long messageId) throws StoreException { if (LOGGER.isDebugEnabled()) { @@ -1226,7 +1281,7 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore * * @throws StoreException If the operation fails for any reason, or if the specified message does not exist. */ - public int getContent(long messageId, int offset, ByteBuffer dst) throws StoreException + int getContent(long messageId, int offset, ByteBuffer dst) throws StoreException { DatabaseEntry contentKeyEntry = new DatabaseEntry(); LongBinding.longToEntry(messageId, contentKeyEntry); @@ -1291,20 +1346,17 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore } } - /** - * Makes the specified configured object persistent. - * - * @param configuredObject Details of the configured object to store. - * @throws StoreException If the operation fails for any reason. - */ - private void storeConfiguredObjectEntry(ConfiguredObjectRecord configuredObject) throws StoreException + private void storeConfiguredObjectEntry(final Transaction txn, ConfiguredObjectRecord configuredObject) throws StoreException { if (_configurationStoreStateManager.isInState(State.ACTIVE)) { - LOGGER.debug("Storing configured object: " + configuredObject); + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Storing configured object: " + configuredObject); + } DatabaseEntry key = new DatabaseEntry(); - UUIDTupleBinding keyBinding = UUIDTupleBinding.getInstance(); - keyBinding.objectToEntry(configuredObject.getId(), key); + UUIDTupleBinding uuidBinding = UUIDTupleBinding.getInstance(); + uuidBinding.objectToEntry(configuredObject.getId(), key); DatabaseEntry value = new DatabaseEntry(); ConfiguredObjectBinding queueBinding = ConfiguredObjectBinding.getInstance(); @@ -1312,12 +1364,13 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore queueBinding.objectToEntry(configuredObject, value); try { - OperationStatus status = getConfiguredObjectsDb().put(null, key, value); + OperationStatus status = getConfiguredObjectsDb().put(txn, key, value); if (status != OperationStatus.SUCCESS) { throw new StoreException("Error writing configured object " + configuredObject + " to database: " + status); } + writeHierarchyRecords(txn, configuredObject); } catch (DatabaseException e) { @@ -1326,26 +1379,54 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore } } } - - private OperationStatus removeConfiguredObject(Transaction tx, UUID id) throws StoreException + + private void writeHierarchyRecords(final Transaction txn, final ConfiguredObjectRecord configuredObject) { + OperationStatus status; + HierarchyKeyBinding hierarchyBinding = HierarchyKeyBinding.getInstance(); + DatabaseEntry hierarchyKey = new DatabaseEntry(); + DatabaseEntry hierarchyValue = new DatabaseEntry(); - LOGGER.debug("Removing configured object: " + id); + for(Map.Entry<String, ConfiguredObjectRecord> parent : configuredObject.getParents().entrySet()) + { + + hierarchyBinding.objectToEntry(new HierarchyKey(configuredObject.getId(), parent.getKey()), hierarchyKey); + UUIDTupleBinding.getInstance().objectToEntry(parent.getValue().getId(), hierarchyValue); + status = getConfiguredObjectHierarchyDb().put(txn, hierarchyKey, hierarchyValue); + if (status != OperationStatus.SUCCESS) + { + throw new StoreException("Error writing configured object " + configuredObject + " parent record to database: " + + status); + } + } + } + + private OperationStatus removeConfiguredObject(Transaction tx, ConfiguredObjectRecord record) throws StoreException + { + UUID id = record.getId(); + Map<String, ConfiguredObjectRecord> parents = record.getParents(); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Removing configured object: " + id); + } DatabaseEntry key = new DatabaseEntry(); UUIDTupleBinding uuidBinding = UUIDTupleBinding.getInstance(); uuidBinding.objectToEntry(id, key); - try - { - return getConfiguredObjectsDb().delete(tx, key); - } - catch (DatabaseException e) + OperationStatus status = getConfiguredObjectsDb().delete(tx, key); + if(status == OperationStatus.SUCCESS) { - throw _environmentFacade.handleDatabaseException("Error deleting of configured object with id " + id + " from database", e); + for(String parentType : parents.keySet()) + { + DatabaseEntry hierarchyKey = new DatabaseEntry(); + HierarchyKeyBinding keyBinding = HierarchyKeyBinding.getInstance(); + keyBinding.objectToEntry(new HierarchyKey(record.getId(), parentType), hierarchyKey); + getConfiguredObjectHierarchyDb().delete(tx, hierarchyKey); + } } + return status; } - - private class StoredBDBMessage implements StoredMessage<StorableMessageMetaData> { @@ -1687,14 +1768,19 @@ public class BDBMessageStore implements MessageStore, DurableConfigurationStore return _type; } - private Database getMessageContentDb() + private Database getConfiguredObjectsDb() { - return _environmentFacade.getOpenDatabase(MESSAGE_CONTENT_DB_NAME); + return _environmentFacade.getOpenDatabase(CONFIGURED_OBJECTS_DB_NAME); } - private Database getConfiguredObjectsDb() + private Database getConfiguredObjectHierarchyDb() { - return _environmentFacade.getOpenDatabase(CONFIGURED_OBJECTS_DB_NAME); + return _environmentFacade.getOpenDatabase(CONFIGURED_OBJECT_HIERARCHY_DB_NAME); + } + + private Database getMessageContentDb() + { + return _environmentFacade.getOpenDatabase(MESSAGE_CONTENT_DB_NAME); } private Database getConfigVersionDb() diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/entry/HierarchyKey.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/entry/HierarchyKey.java new file mode 100644 index 0000000000..d1c341447e --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/entry/HierarchyKey.java @@ -0,0 +1,79 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb.entry; + +import java.util.UUID; + +public class HierarchyKey +{ + private final UUID _childId; + private final String _parentType; + + public HierarchyKey(final UUID childId, final String parentType) + { + _childId = childId; + _parentType = parentType; + } + + public UUID getChildId() + { + return _childId; + } + + public String getParentType() + { + return _parentType; + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + final HierarchyKey that = (HierarchyKey) o; + + if (!_childId.equals(that._childId)) + { + return false; + } + if (!_parentType.equals(that._parentType)) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result = _childId.hashCode(); + result = 31 * result + _parentType.hashCode(); + return result; + } +} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBinding.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBinding.java index bc3beeb78b..38a2215fe7 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBinding.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBinding.java @@ -32,6 +32,7 @@ import com.sleepycat.bind.tuple.TupleBinding; import com.sleepycat.bind.tuple.TupleInput; import com.sleepycat.bind.tuple.TupleOutput; import org.apache.qpid.server.store.StoreException; +import org.apache.qpid.server.store.berkeleydb.BDBConfiguredObjectRecord; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonProcessingException; @@ -80,7 +81,7 @@ public class ConfiguredObjectBinding extends TupleBinding<ConfiguredObjectRecord _uuid = uuid; } - public ConfiguredObjectRecord entryToObject(TupleInput tupleInput) + public BDBConfiguredObjectRecord entryToObject(TupleInput tupleInput) { String type = tupleInput.readString(); String json = tupleInput.readString(); @@ -88,7 +89,7 @@ public class ConfiguredObjectBinding extends TupleBinding<ConfiguredObjectRecord try { Map<String,Object> value = mapper.readValue(json, Map.class); - ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(_uuid, type, value); + BDBConfiguredObjectRecord configuredObject = new BDBConfiguredObjectRecord(_uuid, type, value); return configuredObject; } catch (IOException e) diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/HierarchyKeyBinding.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/HierarchyKeyBinding.java new file mode 100644 index 0000000000..13adaabfc8 --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/HierarchyKeyBinding.java @@ -0,0 +1,59 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store.berkeleydb.tuple; + +import com.sleepycat.bind.tuple.TupleBinding; +import com.sleepycat.bind.tuple.TupleInput; +import com.sleepycat.bind.tuple.TupleOutput; +import org.apache.qpid.server.store.berkeleydb.entry.HierarchyKey; +import org.apache.qpid.server.store.berkeleydb.entry.QueueEntryKey; + +import java.util.UUID; + +public class HierarchyKeyBinding extends TupleBinding<HierarchyKey> +{ + + private static final HierarchyKeyBinding INSTANCE = new HierarchyKeyBinding(); + + public static HierarchyKeyBinding getInstance() + { + return INSTANCE; + } + + /** private constructor forces getInstance instead */ + private HierarchyKeyBinding() { } + + public HierarchyKey entryToObject(TupleInput tupleInput) + { + UUID childId = new UUID(tupleInput.readLong(), tupleInput.readLong()); + String parentType = tupleInput.readString(); + + return new HierarchyKey(childId, parentType); + } + + public void objectToEntry(HierarchyKey hk, TupleOutput tupleOutput) + { + UUID uuid = hk.getChildId(); + tupleOutput.writeLong(uuid.getMostSignificantBits()); + tupleOutput.writeLong(uuid.getLeastSignificantBits()); + tupleOutput.writeString(hk.getParentType()); + } +}
\ No newline at end of file diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java index adcaef35ef..0ff90a6d77 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/StoreUpgrade.java @@ -22,7 +22,9 @@ package org.apache.qpid.server.store.berkeleydb.upgrade; import com.sleepycat.je.Environment; +import org.apache.qpid.server.model.ConfiguredObject; + public interface StoreUpgrade { - void performUpgrade(Environment environment, UpgradeInteractionHandler handler, String virtualHostName); + void performUpgrade(Environment environment, UpgradeInteractionHandler handler, ConfiguredObject<?> parent); } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4To5.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4To5.java index 87f8afde4a..3588b96e88 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4To5.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4To5.java @@ -39,6 +39,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.MessagePublishInfo; +import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.protocol.v0_8.MessageMetaData; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.store.StorableMessageMetaData; @@ -74,7 +75,7 @@ public class UpgradeFrom4To5 extends AbstractStoreUpgrade private static final Logger _logger = Logger.getLogger(UpgradeFrom4To5.class); - public void performUpgrade(final Environment environment, final UpgradeInteractionHandler handler, String virtualHostName) + public void performUpgrade(final Environment environment, final UpgradeInteractionHandler handler, ConfiguredObject<?> parent) { Transaction transaction = null; reportStarting(environment, 4); diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java index 46f2afd741..366b6a1c97 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6.java @@ -40,6 +40,7 @@ import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Queue; @@ -118,11 +119,11 @@ public class UpgradeFrom5To6 extends AbstractStoreUpgrade * Queue, Exchange, Bindings entries are stored now as configurable objects * in "CONFIGURED_OBJECTS" table. */ - public void performUpgrade(final Environment environment, final UpgradeInteractionHandler handler, String virtualHostName) + public void performUpgrade(final Environment environment, final UpgradeInteractionHandler handler, ConfiguredObject<?> parent) { reportStarting(environment, 5); upgradeMessages(environment, handler); - upgradeConfiguredObjectsAndDependencies(environment, handler, virtualHostName); + upgradeConfiguredObjectsAndDependencies(environment, handler, parent.getName()); renameDatabases(environment, null); reportFinished(environment, 6); } diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom6To7.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom6To7.java index ce00fd1a48..9dcd291b9d 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom6To7.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom6To7.java @@ -27,6 +27,8 @@ import com.sleepycat.je.DatabaseConfig; import com.sleepycat.je.DatabaseEntry; import com.sleepycat.je.Environment; import com.sleepycat.je.OperationStatus; + +import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.store.StoreException; public class UpgradeFrom6To7 extends AbstractStoreUpgrade @@ -35,7 +37,7 @@ public class UpgradeFrom6To7 extends AbstractStoreUpgrade private static final int DEFAULT_CONFIG_VERSION = 0; @Override - public void performUpgrade(Environment environment, UpgradeInteractionHandler handler, String virtualHostName) + public void performUpgrade(Environment environment, UpgradeInteractionHandler handler, ConfiguredObject<?> parent) { reportStarting(environment, 6); DatabaseConfig dbConfig = new DatabaseConfig(); diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom7To8.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom7To8.java new file mode 100644 index 0000000000..413acc90c4 --- /dev/null +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom7To8.java @@ -0,0 +1,160 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.store.berkeleydb.upgrade; + +import com.sleepycat.bind.tuple.ByteBinding; +import com.sleepycat.bind.tuple.IntegerBinding; +import com.sleepycat.bind.tuple.TupleBinding; +import com.sleepycat.bind.tuple.TupleInput; +import com.sleepycat.bind.tuple.TupleOutput; +import com.sleepycat.je.*; + +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.store.ConfiguredObjectRecord; +import org.apache.qpid.server.store.StoreException; +import org.apache.qpid.server.store.berkeleydb.BDBConfiguredObjectRecord; +import org.apache.qpid.server.store.berkeleydb.entry.HierarchyKey; +import org.apache.qpid.server.store.berkeleydb.tuple.ConfiguredObjectBinding; +import org.apache.qpid.server.store.berkeleydb.tuple.HierarchyKeyBinding; +import org.apache.qpid.server.store.berkeleydb.tuple.UUIDTupleBinding; +import org.codehaus.jackson.map.ObjectMapper; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class UpgradeFrom7To8 extends AbstractStoreUpgrade +{ + + @Override + public void performUpgrade(Environment environment, UpgradeInteractionHandler handler, ConfiguredObject<?> parent) + { + reportStarting(environment, 7); + + DatabaseConfig dbConfig = new DatabaseConfig(); + dbConfig.setTransactional(true); + dbConfig.setAllowCreate(true); + + Database hierarchyDb = environment.openDatabase(null, "CONFIGURED_OBJECT_HIERARCHY", dbConfig); + Database configuredObjectsDb = environment.openDatabase(null, "CONFIGURED_OBJECTS", dbConfig); + + Cursor objectsCursor = null; + + Transaction txn = environment.beginTransaction(null, null); + + try + { + objectsCursor = configuredObjectsDb.openCursor(txn, null); + DatabaseEntry key = new DatabaseEntry(); + DatabaseEntry value = new DatabaseEntry(); + + Map<UUID, BDBConfiguredObjectRecord> configuredObjects = + new HashMap<UUID, BDBConfiguredObjectRecord>(); + + while (objectsCursor.getNext(key, value, LockMode.RMW) == OperationStatus.SUCCESS) + { + UUID id = UUIDTupleBinding.getInstance().entryToObject(key); + TupleInput input = TupleBinding.entryToInput(value); + String type = input.readString(); + + if(!type.endsWith("Binding")) + { + UUIDTupleBinding.getInstance().objectToEntry(parent.getId(),value); + TupleOutput tupleOutput = new TupleOutput(); + tupleOutput.writeLong(id.getMostSignificantBits()); + tupleOutput.writeLong(id.getLeastSignificantBits()); + tupleOutput.writeString("VirtualHost"); + TupleBinding.outputToEntry(tupleOutput, key); + hierarchyDb.put(txn, key, value); + } + else + { + String json = input.readString(); + ObjectMapper mapper = new ObjectMapper(); + try + { + DatabaseEntry hierarchyKey = new DatabaseEntry(); + DatabaseEntry hierarchyValue = new DatabaseEntry(); + + Map<String,Object> attributes = mapper.readValue(json, Map.class); + Object queueIdString = attributes.remove("queue"); + if(queueIdString instanceof String) + { + UUID queueId = UUID.fromString(queueIdString.toString()); + UUIDTupleBinding.getInstance().objectToEntry(queueId,hierarchyValue); + TupleOutput tupleOutput = new TupleOutput(); + tupleOutput.writeLong(id.getMostSignificantBits()); + tupleOutput.writeLong(id.getLeastSignificantBits()); + tupleOutput.writeString("Queue"); + TupleBinding.outputToEntry(tupleOutput, hierarchyKey); + hierarchyDb.put(txn, hierarchyKey, hierarchyValue); + } + Object exchangeIdString = attributes.remove("exchange"); + if(exchangeIdString instanceof String) + { + UUID exchangeId = UUID.fromString(exchangeIdString.toString()); + UUIDTupleBinding.getInstance().objectToEntry(exchangeId,hierarchyValue); + TupleOutput tupleOutput = new TupleOutput(); + tupleOutput.writeLong(id.getMostSignificantBits()); + tupleOutput.writeLong(id.getLeastSignificantBits()); + tupleOutput.writeString("Exchange"); + TupleBinding.outputToEntry(tupleOutput, hierarchyKey); + hierarchyDb.put(txn, hierarchyKey, hierarchyValue); + } + TupleOutput tupleOutput = new TupleOutput(); + tupleOutput.writeString(type); + StringWriter writer = new StringWriter(); + mapper.writeValue(writer,attributes); + tupleOutput.writeString(writer.getBuffer().toString()); + TupleBinding.outputToEntry(tupleOutput, value); + objectsCursor.putCurrent(value); + } + catch (IOException e) + { + throw new StoreException(e); + } + + } + + + } + + + } + finally + { + if(objectsCursor != null) + { + objectsCursor.close(); + } + } + txn.commit(); + + hierarchyDb.close(); + configuredObjectsDb.close(); + + + + reportFinished(environment, 8); + } +} diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java index 7852e2d703..e80d60609f 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/upgrade/Upgrader.java @@ -26,6 +26,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.apache.log4j.Logger; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.store.berkeleydb.BDBMessageStore; @@ -45,12 +47,12 @@ public class Upgrader static final String VERSION_DB_NAME = "DB_VERSION"; private Environment _environment; - private String _virtualHostName; + private ConfiguredObject<?> _parent; - public Upgrader(Environment environment, String virtualHostName) + public Upgrader(Environment environment, ConfiguredObject<?> parent) { _environment = environment; - _virtualHostName = virtualHostName; + _parent = parent; } public void upgradeIfNecessary() @@ -158,7 +160,7 @@ public class Upgrader + "UpgradeFrom"+fromVersion+"To"+toVersion); Constructor<StoreUpgrade> ctr = upgradeClass.getConstructor(); StoreUpgrade upgrade = ctr.newInstance(); - upgrade.performUpgrade(_environment, UpgradeInteractionHandler.DEFAULT_HANDLER, _virtualHostName); + upgrade.performUpgrade(_environment, UpgradeInteractionHandler.DEFAULT_HANDLER, _parent); } catch (ClassNotFoundException e) { diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBindingTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBindingTest.java index 5a5d39081c..965cad1cb5 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBindingTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/tuple/ConfiguredObjectBindingTest.java @@ -29,6 +29,7 @@ import org.apache.qpid.server.store.ConfiguredObjectRecord; import com.sleepycat.bind.tuple.TupleInput; import com.sleepycat.bind.tuple.TupleOutput; +import org.apache.qpid.server.store.ConfiguredObjectRecordImpl; public class ConfiguredObjectBindingTest extends TestCase { @@ -46,7 +47,7 @@ public class ConfiguredObjectBindingTest extends TestCase { super.setUp(); _configuredObjectBinding = ConfiguredObjectBinding.getInstance(); - _object = new ConfiguredObjectRecord(UUIDGenerator.generateRandomUUID(), DUMMY_TYPE_STRING, + _object = new ConfiguredObjectRecordImpl(UUIDGenerator.generateRandomUUID(), DUMMY_TYPE_STRING, DUMMY_ATTRIBUTES_MAP); } diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java index b2b28b3c2d..ce143aba1b 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/AbstractUpgradeTestCase.java @@ -25,12 +25,16 @@ import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPrepare import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.PRIORITY_QUEUE_NAME; import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.QUEUE_NAME; import static org.apache.qpid.server.store.berkeleydb.BDBStoreUpgradeTestPreparer.QUEUE_WITH_DLQ_NAME; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.io.File; import java.io.InputStream; +import java.util.UUID; import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.subjects.TestBlankSubject; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.util.FileUtils; @@ -167,8 +171,11 @@ public abstract class AbstractUpgradeTestCase extends QpidTestCase return count.longValue(); } - public String getVirtualHostName() + public VirtualHost getVirtualHost() { - return getName(); + VirtualHost virtualHost = mock(VirtualHost.class); + when(virtualHost.getName()).thenReturn(getName()); + when(virtualHost.getId()).thenReturn(UUID.randomUUID()); + return virtualHost; } } diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java index 500fb0a919..d0f9455d9a 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom4to5Test.java @@ -73,7 +73,7 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase public void testPerformUpgradeWithHandlerAnsweringYes() throws Exception { UpgradeFrom4To5 upgrade = new UpgradeFrom4To5(); - upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.YES), getVirtualHostName()); + upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.YES), getVirtualHost()); assertQueues(new HashSet<String>(Arrays.asList(QUEUE_NAMES))); @@ -103,7 +103,7 @@ public class UpgradeFrom4to5Test extends AbstractUpgradeTestCase public void testPerformUpgradeWithHandlerAnsweringNo() throws Exception { UpgradeFrom4To5 upgrade = new UpgradeFrom4To5(); - upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.NO), getVirtualHostName()); + upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.NO), getVirtualHost()); HashSet<String> queues = new HashSet<String>(Arrays.asList(QUEUE_NAMES)); assertTrue(NON_DURABLE_QUEUE_NAME + " should be in the list of queues" , queues.remove(NON_DURABLE_QUEUE_NAME)); diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java index 701fd94115..0460b1ce4c 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgradeFrom5To6Test.java @@ -87,7 +87,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase public void testPerformUpgrade() throws Exception { UpgradeFrom5To6 upgrade = new UpgradeFrom5To6(); - upgrade.performUpgrade(_environment, UpgradeInteractionHandler.DEFAULT_HANDLER, getVirtualHostName()); + upgrade.performUpgrade(_environment, UpgradeInteractionHandler.DEFAULT_HANDLER, getVirtualHost()); assertDatabaseRecordCounts(); assertContent(); @@ -101,7 +101,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase corruptDatabase(); UpgradeFrom5To6 upgrade = new UpgradeFrom5To6(); - upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.YES), getVirtualHostName()); + upgrade.performUpgrade(_environment, new StaticAnswerHandler(UpgradeInteractionResponse.YES), getVirtualHost()); assertDatabaseRecordCounts(); @@ -117,7 +117,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase UpgradeInteractionHandler discardMessageInteractionHandler = new StaticAnswerHandler(UpgradeInteractionResponse.NO); - upgrade.performUpgrade(_environment, discardMessageInteractionHandler, getVirtualHostName()); + upgrade.performUpgrade(_environment, discardMessageInteractionHandler, getVirtualHost()); assertDatabaseRecordCount(NEW_METADATA_DB_NAME, 12); assertDatabaseRecordCount(NEW_CONTENT_DB_NAME, 12); @@ -135,7 +135,7 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase { populateOldXidEntries(environment); UpgradeFrom5To6 upgrade = new UpgradeFrom5To6(); - upgrade.performUpgrade(environment, UpgradeInteractionHandler.DEFAULT_HANDLER, getVirtualHostName()); + upgrade.performUpgrade(environment, UpgradeInteractionHandler.DEFAULT_HANDLER, getVirtualHost()); assertXidEntries(environment); } finally @@ -171,11 +171,11 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase NewRecordImpl[] newDequeues = newTransaction.getDequeues(); assertEquals("Unxpected new enqueus number", 1, newEnqueues.length); NewRecordImpl enqueue = newEnqueues[0]; - assertEquals("Unxpected queue id", UUIDGenerator.generateQueueUUID("TEST1", getVirtualHostName()), enqueue.getId()); + assertEquals("Unxpected queue id", UUIDGenerator.generateQueueUUID("TEST1", getVirtualHost().getName()), enqueue.getId()); assertEquals("Unxpected message id", 1, enqueue.getMessageNumber()); assertEquals("Unxpected new dequeues number", 1, newDequeues.length); NewRecordImpl dequeue = newDequeues[0]; - assertEquals("Unxpected queue id", UUIDGenerator.generateQueueUUID("TEST2", getVirtualHostName()), dequeue.getId()); + assertEquals("Unxpected queue id", UUIDGenerator.generateQueueUUID("TEST2", getVirtualHost().getName()), dequeue.getId()); assertEquals("Unxpected message id", 2, dequeue.getMessageNumber()); } @@ -347,13 +347,13 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase { String exchangeName = (String) deserialized.get(Exchange.NAME); assertNotNull(exchangeName); - assertEquals("Unexpected key", key, UUIDGenerator.generateExchangeUUID(exchangeName, getVirtualHostName())); + assertEquals("Unexpected key", key, UUIDGenerator.generateExchangeUUID(exchangeName, getVirtualHost().getName())); } else if (type.equals(Queue.class.getName())) { String queueName = (String) deserialized.get(Queue.NAME); assertNotNull(queueName); - assertEquals("Unexpected key", key, UUIDGenerator.generateQueueUUID(queueName, getVirtualHostName())); + assertEquals("Unexpected key", key, UUIDGenerator.generateQueueUUID(queueName, getVirtualHost().getName())); } else if (type.equals(Binding.class.getName())) { @@ -368,15 +368,15 @@ public class UpgradeFrom5To6Test extends AbstractUpgradeTestCase private Map<String, Object> createExpectedQueueBindingMapAndID(String queue, String bindingName, String exchangeName, Map<String, String> argumentMap, List<UUID> expectedBindingIDs) { Map<String, Object> expectedQueueBinding = new HashMap<String, Object>(); - expectedQueueBinding.put(Binding.QUEUE, UUIDGenerator.generateQueueUUID(queue, getVirtualHostName()).toString()); + expectedQueueBinding.put(Binding.QUEUE, UUIDGenerator.generateQueueUUID(queue, getVirtualHost().getName()).toString()); expectedQueueBinding.put(Binding.NAME, bindingName); - expectedQueueBinding.put(Binding.EXCHANGE, UUIDGenerator.generateExchangeUUID(exchangeName, getVirtualHostName()).toString()); + expectedQueueBinding.put(Binding.EXCHANGE, UUIDGenerator.generateExchangeUUID(exchangeName, getVirtualHost().getName()).toString()); if (argumentMap != null) { expectedQueueBinding.put(Binding.ARGUMENTS, argumentMap); } - expectedBindingIDs.add(UUIDGenerator.generateBindingUUID(exchangeName, queue, bindingName, getVirtualHostName())); + expectedBindingIDs.add(UUIDGenerator.generateBindingUUID(exchangeName, queue, bindingName, getVirtualHost().getName())); return expectedQueueBinding; } diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java index 810f4a1fca..c407be50c6 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderFailOnNewerVersionTest.java @@ -43,7 +43,7 @@ public class UpgraderFailOnNewerVersionTest extends AbstractUpgradeTestCase public void setUp() throws Exception { super.setUp(); - _upgrader = new Upgrader(_environment, getVirtualHostName()); + _upgrader = new Upgrader(_environment, getVirtualHost()); } private int getStoreVersion() diff --git a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderTest.java b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderTest.java index 3465f3582f..4b9a8d19a8 100644 --- a/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderTest.java +++ b/qpid/java/bdbstore/src/test/java/org/apache/qpid/server/store/berkeleydb/upgrade/UpgraderTest.java @@ -51,7 +51,7 @@ public class UpgraderTest extends AbstractUpgradeTestCase public void setUp() throws Exception { super.setUp(); - _upgrader = new Upgrader(_environment, getVirtualHostName()); + _upgrader = new Upgrader(_environment, getVirtualHost()); } private int getStoreVersion(Environment environment) @@ -108,7 +108,7 @@ public class UpgraderTest extends AbstractUpgradeTestCase Environment emptyEnvironment = createEnvironment(nonExistentStoreLocation); try { - _upgrader = new Upgrader(emptyEnvironment, getVirtualHostName()); + _upgrader = new Upgrader(emptyEnvironment, getVirtualHost()); _upgrader.upgradeIfNecessary(); List<String> databaseNames = emptyEnvironment.getDatabaseNames(); diff --git a/qpid/java/bdbstore/systests/pom.xml b/qpid/java/bdbstore/systests/pom.xml index e8620d3426..fe718f9dac 100644 --- a/qpid/java/bdbstore/systests/pom.xml +++ b/qpid/java/bdbstore/systests/pom.xml @@ -33,6 +33,7 @@ <test.log4j.configuration.file>${project.basedir}${file.separator}..${file.separator}..${file.separator}test-profiles${file.separator}log4j-test.xml</test.log4j.configuration.file> <test.working.directory>${basedir}/../..</test.working.directory> <test.resource.directory>${basedir}/../..</test.resource.directory> + <test.systest.resource.directory>${basedir}/../../systests</test.systest.resource.directory> </properties> <dependencies> @@ -65,6 +66,66 @@ <groupId>com.sleepycat</groupId> <artifactId>je</artifactId> </dependency> + + <dependency> + <groupId>org.apache.qpid</groupId> + <artifactId>qpid-bdbstore</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-resources-plugin</artifactId> + <!--version specified in parent pluginManagement --> + <executions> + <!-- copy the bdbstore bin contents to where the tests expect them --> + <execution> + <id>copy-bdbstore-bin-resources</id> + <phase>generate-resources</phase> + <goals> + <goal>copy-resources</goal> + </goals> + <configuration> + <outputDirectory>${qpid.home}</outputDirectory> + <resources> + <resource> + <directory>${basedir}/..</directory> + <includes> + <include>bin/</include> + </includes> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <!-- fix the fact that the maven-resources-plugin copy-resources doesn't maintain file permissions in unix --> + <execution> + <id>fix-bdb-script-permissions</id> + <phase>package</phase> + <configuration> + <target> + <chmod perm="755"> + <fileset dir="${qpid.home}"> + <include name="bin/**"/> + </fileset> + </chmod> + </target> + </configuration> + <goals><goal>run</goal></goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + </project> diff --git a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java index c8fcfe0826..ba84d0682a 100644 --- a/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java +++ b/qpid/java/bdbstore/systests/src/main/java/org/apache/qpid/server/store/berkeleydb/BDBMessageStoreTest.java @@ -236,6 +236,7 @@ public class BDBMessageStoreTest extends MessageStoreTest { messageStore.closeMessageStore(); + BDBMessageStore newStore = new BDBMessageStore(); MessageStoreRecoveryHandler recoveryHandler = mock(MessageStoreRecoveryHandler.class); @@ -243,7 +244,7 @@ public class BDBMessageStoreTest extends MessageStoreTest VirtualHost<?> virtualHost = getVirtualHostModel(); newStore.openMessageStore(virtualHost.getName(), virtualHost.getMessageStoreSettings()); - newStore.recoverMessageStore(recoveryHandler, null); + newStore.recoverMessageStore(getVirtualHostModel(), recoveryHandler, null); return newStore; } diff --git a/qpid/java/broker-core/src/main/java/broker.bnd b/qpid/java/broker-core/src/main/java/broker.bnd index 1a371a5efd..8b3f663ec6 100755 --- a/qpid/java/broker-core/src/main/java/broker.bnd +++ b/qpid/java/broker-core/src/main/java/broker.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.27.0 +ver: 0.29.0 Bundle-SymbolicName: qpid-broker Bundle-Version: ${ver} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java index ffa1d65ec2..0ec6a31253 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/Broker.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Properties; import java.util.Set; +import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.qpid.server.configuration.ConfigurationEntryStore; @@ -41,7 +42,6 @@ import org.apache.qpid.server.logging.messages.BrokerMessages; 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.TaskPrincipal; import javax.security.auth.Subject; @@ -52,6 +52,7 @@ public class Broker private volatile Thread _shutdownHookThread; private volatile IApplicationRegistry _applicationRegistry; private EventLogger _eventLogger; + private boolean _configuringOwnLogging = false; protected static class InitException extends RuntimeException { @@ -71,11 +72,20 @@ public class Broker } finally { - if (_applicationRegistry != null) + try { - _applicationRegistry.close(); + if (_applicationRegistry != null) + { + _applicationRegistry.close(); + } + } + finally + { + if (_configuringOwnLogging) + { + LogManager.shutdown(); + } } - } } @@ -166,6 +176,7 @@ public class Broker private void configureLogging(File logConfigFile, int logWatchTime) throws InitException, IOException { + _configuringOwnLogging = true; if (logConfigFile.exists() && logConfigFile.canRead()) { _eventLogger.message(BrokerMessages.LOG_CONFIG(logConfigFile.getAbsolutePath())); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java index 8eec88d556..46f3cd458b 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.PreferencesProvider; +import org.apache.qpid.server.model.User; import org.apache.qpid.server.model.adapter.AuthenticationProviderFactory; public class AuthenticationProviderRecoverer implements ConfiguredObjectRecoverer<AuthenticationProvider> @@ -68,9 +69,24 @@ public class AuthenticationProviderRecoverer implements ConfiguredObjectRecovere Map<String, Collection<ConfigurationEntry>> childEntries, String type) { - ConfiguredObjectRecoverer<?> recoverer = recovererProvider.getRecoverer(type); + ConfiguredObjectRecoverer<?> recoverer = null; + + if(authenticationProvider instanceof RecovererProvider) + { + recoverer = ((RecovererProvider)authenticationProvider).getRecoverer(type); + } + + if(recoverer == null) + { + recoverer = recovererProvider.getRecoverer(type); + } + if (recoverer == null) { + if(authenticationProvider instanceof RecovererProvider) + { + ((RecovererProvider)authenticationProvider).getRecoverer(type); + } throw new IllegalConfigurationException("Cannot recover entry for the type '" + type + "' from broker"); } Collection<ConfigurationEntry> entries = childEntries.get(type); @@ -85,6 +101,10 @@ public class AuthenticationProviderRecoverer implements ConfiguredObjectRecovere { authenticationProvider.setPreferencesProvider((PreferencesProvider)object); } + else if(object instanceof User) + { + authenticationProvider.recoverUser((User)object); + } else { throw new IllegalConfigurationException("Cannot associate " + object + " with authentication provider " + authenticationProvider); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java index e030d50e56..addc42e6f9 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java @@ -87,7 +87,7 @@ public class StoreConfigurationChangeListener implements ConfigurationChangeList private ConfigurationEntry toConfigurationEntry(ConfiguredObject object) { - Class<? extends ConfiguredObject> objectType = getConfiguredObjectType(object); + Class<? extends ConfiguredObject> objectType = object.getCategoryClass(); Set<UUID> childrenIds = getChildrenIds(object, objectType); ConfigurationEntry entry = new ConfigurationEntry(object.getId(), objectType.getSimpleName(), object.getActualAttributes(), childrenIds, _store); @@ -120,98 +120,6 @@ public class StoreConfigurationChangeListener implements ConfigurationChangeList return childrenIds; } - private Class<? extends ConfiguredObject> getConfiguredObjectType(ConfiguredObject object) - { - if (object instanceof Broker) - { - return Broker.class; - } - else if (object instanceof VirtualHost) - { - return VirtualHost.class; - } - else if (object instanceof Port) - { - return Port.class; - } - else if (object instanceof AuthenticationProvider) - { - return AuthenticationProvider.class; - } - return getConfiguredObjectTypeFromImplementedInterfaces(object.getClass()); - } - - @SuppressWarnings("unchecked") - private Class<? extends ConfiguredObject> getConfiguredObjectTypeFromImplementedInterfaces(Class<?> objectClass) - { - // get all implemented interfaces extending ConfiguredObject - Set<Class<?>> interfaces = getImplementedInterfacesExtendingSuper(objectClass, ConfiguredObject.class); - - if (interfaces.size() == 0) - { - throw new StoreException("Can not identify the configured object type"); - } - - if (interfaces.size() == 1) - { - return (Class<? extends ConfiguredObject>)interfaces.iterator().next(); - } - - Set<Class<?>> superInterfaces = new HashSet<Class<?>>(); - - // find all super interfaces - for (Class<?> interfaceClass : interfaces) - { - for (Class<?> interfaceClass2 : interfaces) - { - if (interfaceClass != interfaceClass2) - { - if (interfaceClass.isAssignableFrom(interfaceClass2)) - { - superInterfaces.add(interfaceClass); - } - } - } - } - - // remove super interfaces - for (Class<?> superInterface : superInterfaces) - { - interfaces.remove(superInterface); - } - - if (interfaces.size() == 1) - { - return (Class<? extends ConfiguredObject>)interfaces.iterator().next(); - } - else - { - throw new StoreException("Can not identify the configured object type as an it implements" - + " more than one configured object interfaces: " + interfaces); - } - - } - - private Set<Class<?>> getImplementedInterfacesExtendingSuper(Class<?> classInstance, Class<?> superInterface) - { - Set<Class<?>> interfaces = new HashSet<Class<?>>(); - Class<?>[] classInterfaces = classInstance.getInterfaces(); - for (Class<?> interfaceClass : classInterfaces) - { - if (interfaceClass!= superInterface && superInterface.isAssignableFrom(interfaceClass)) - { - interfaces.add(interfaceClass); - } - } - Class<?> superClass = classInstance.getSuperclass(); - if (superClass != null) - { - Set<Class<?>> superClassInterfaces = getImplementedInterfacesExtendingSuper(superClass, superInterface); - interfaces.addAll(superClassInterfaces); - } - return interfaces; - } - @Override public String toString() { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index a30806d810..25f20ba1ee 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -612,7 +612,7 @@ public abstract class AbstractExchange<T extends AbstractExchange<T>> } // Check access - _virtualHost.getSecurityManager().authoriseUnbind(this, bindingKey, queue); + _virtualHost.getSecurityManager().authoriseUnbind(binding); BindingImpl b = _bindingsMap.remove(new BindingIdentifier(bindingKey,queue)); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java index 8e1ea39cec..fc0a8ab7e5 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java @@ -55,4 +55,6 @@ public interface AuthenticationProvider<X extends AuthenticationProvider<X>> ext * @param preferencesProvider */ void setPreferencesProvider(PreferencesProvider preferencesProvider); + + void recoverUser(User user); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java index b674f1e7db..7e3e5c9bbe 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.model; import org.apache.qpid.server.model.adapter.AbstractConfiguredObject; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import java.security.AccessControlException; import java.util.Collection; @@ -276,4 +277,10 @@ public interface ConfiguredObject<X extends ConfiguredObject<X>> ConfiguredObject... otherParents); void setAttributes(Map<String, Object> attributes) throws IllegalStateException, AccessControlException, IllegalArgumentException; + + Class<? extends ConfiguredObject> getCategoryClass(); + + // TODO - remove this when objects become responsible for their own storage + ConfiguredObjectRecord asObjectRecord(); + } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java index 6e8932d3ba..103602edf5 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java @@ -26,9 +26,10 @@ import java.util.Map; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.message.MessageInstance; -import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.store.MessageStore; +import java.util.UUID; + @ManagedObject( managesChildren = true ) public interface VirtualHost<X extends VirtualHost<X>> extends ConfiguredObject<X> { @@ -173,15 +174,11 @@ public interface VirtualHost<X extends VirtualHost<X>> extends ConfiguredObject< void executeTransaction(TransactionalOperation op); - /** - * A temporary hack to expose host security manager. - * TODO We need to add and implement an authorization provider configured object instead - */ - SecurityManager getSecurityManager(); - // TODO - remove this TaskExecutor getTaskExecutor(); + Exchange getExchange(UUID id); + MessageStore getMessageStore(); String getType(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java index d263bb45b2..52208f7d7f 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/AbstractConfiguredObject.java @@ -26,6 +26,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessControlException; import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.*; import org.apache.qpid.server.model.*; @@ -37,6 +38,7 @@ import org.apache.qpid.server.configuration.updater.SetAttributeTask; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.util.MapValueConverter; import org.apache.qpid.server.util.ServerScopedRuntimeException; @@ -212,7 +214,7 @@ public abstract class AbstractConfiguredObject<X extends ConfiguredObject<X>> im final AuthenticatedPrincipal currentUser = SecurityManager.getCurrentUser(); if(currentUser != null) { - _attributes.put(CREATED_BY, currentUser); + _attributes.put(CREATED_BY, currentUser.getName()); } } if(!_attributes.containsKey(CREATED_TIME)) @@ -256,6 +258,11 @@ public abstract class AbstractConfiguredObject<X extends ConfiguredObject<X>> im return _name; } + public Class<? extends ConfiguredObject> getCategoryClass() + { + return getCategory(getClass()); + } + public State getDesiredState() { return null; //TODO @@ -558,6 +565,61 @@ public abstract class AbstractConfiguredObject<X extends ConfiguredObject<X>> im return getClass().getSimpleName() + " [id=" + _id + ", name=" + getName() + "]"; } + public ConfiguredObjectRecord asObjectRecord() + { + return new ConfiguredObjectRecord() + { + @Override + public UUID getId() + { + return AbstractConfiguredObject.this.getId(); + } + + @Override + public String getType() + { + return getCategoryClass().getSimpleName(); + } + + @Override + public Map<String, Object> getAttributes() + { + return Subject.doAs(SecurityManager.getSubjectWithAddedSystemRights(), new PrivilegedAction<Map<String, Object>>() + { + @Override + public Map<String, Object> run() + { + Map<String,Object> actualAttributes = new HashMap<String, Object>(getActualAttributes()); + for(Map.Entry<String,Object> entry : actualAttributes.entrySet()) + { + if(entry.getValue() instanceof ConfiguredObject) + { + entry.setValue(((ConfiguredObject)entry.getValue()).getId()); + } + } + actualAttributes.remove(ID); + return actualAttributes; + } + }); + } + + @Override + public Map<String, ConfiguredObjectRecord> getParents() + { + Map<String, ConfiguredObjectRecord> parents = new LinkedHashMap<String, ConfiguredObjectRecord>(); + for(Class<? extends ConfiguredObject> parentClass : Model.getInstance().getParentTypes(getCategoryClass())) + { + ConfiguredObject parent = getParent(parentClass); + if(parent != null) + { + parents.put(parentClass.getSimpleName(), parent.asObjectRecord()); + } + } + return parents; + } + }; + } + @SuppressWarnings("unchecked") @Override public <C extends ConfiguredObject> C createChild(Class<C> childClass, Map<String, Object> attributes, ConfiguredObject... otherParents) diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java index 2e86d834bb..fafe081226 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java @@ -42,7 +42,6 @@ import org.apache.qpid.server.plugin.ExchangeType; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.ConflationQueue; -import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.store.MessageStore; @@ -1042,12 +1041,6 @@ public final class VirtualHostAdapter extends AbstractConfiguredObject<VirtualHo } @Override - public SecurityManager getSecurityManager() - { - return _virtualHost.getSecurityManager(); - } - - @Override public MessageStore getMessageStore() { return _virtualHost.getMessageStore(); @@ -1089,9 +1082,15 @@ public final class VirtualHostAdapter extends AbstractConfiguredObject<VirtualHo } } + @Override public TaskExecutor getTaskExecutor() { return super.getTaskExecutor(); } + @Override + public Exchange getExchange(UUID id) + { + return _virtualHost.getExchange(id); + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/SecurityManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/SecurityManager.java index 77886e9030..478499fe6c 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/SecurityManager.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/SecurityManager.java @@ -18,27 +18,6 @@ */ package org.apache.qpid.server.security; -import org.apache.log4j.Logger; - -import org.apache.qpid.server.binding.BindingImpl; -import org.apache.qpid.server.consumer.ConsumerImpl; -import org.apache.qpid.server.exchange.ExchangeImpl; - -import org.apache.qpid.server.model.*; -import org.apache.qpid.server.plugin.AccessControlFactory; -import org.apache.qpid.server.plugin.QpidServiceLoader; -import org.apache.qpid.server.protocol.AMQConnectionModel; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; -import org.apache.qpid.server.security.access.ObjectProperties; -import org.apache.qpid.server.security.access.ObjectType; -import org.apache.qpid.server.security.access.Operation; -import org.apache.qpid.server.security.access.OperationLoggingDetails; -import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; -import org.apache.qpid.server.security.auth.TaskPrincipal; - -import javax.security.auth.Subject; - import static org.apache.qpid.server.security.access.ObjectType.BROKER; import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE; import static org.apache.qpid.server.security.access.ObjectType.GROUP; @@ -62,54 +41,48 @@ import java.security.AccessController; import java.security.Principal; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import javax.security.auth.Subject; + +import org.apache.qpid.server.binding.BindingImpl; +import org.apache.qpid.server.consumer.ConsumerImpl; +import org.apache.qpid.server.exchange.ExchangeImpl; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfigurationChangeListener; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectProperties.Property; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.access.OperationLoggingDetails; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; +import org.apache.qpid.server.security.auth.TaskPrincipal; + public class SecurityManager implements ConfigurationChangeListener { - private static final Logger _logger = Logger.getLogger(SecurityManager.class); - private static final Subject SYSTEM = new Subject(true, Collections.singleton(new SystemPrincipal()), Collections.emptySet(), Collections.emptySet()); + private final ConcurrentHashMap<String, AccessControl> _plugins = new ConcurrentHashMap<String, AccessControl>(); + private final boolean _managementMode; + private final Broker<?> _broker; - private ConcurrentHashMap<String, AccessControl> _globalPlugins = new ConcurrentHashMap<String, AccessControl>(); - private ConcurrentHashMap<String, AccessControl> _hostPlugins = new ConcurrentHashMap<String, AccessControl>(); - - private boolean _managementMode; - - private Broker _broker; + private final ConcurrentHashMap<PublishAccessCheckCacheEntry, PublishAccessCheck> _publishAccessCheckCache = new ConcurrentHashMap<SecurityManager.PublishAccessCheckCacheEntry, SecurityManager.PublishAccessCheck>(); - /* - * Used by the Broker. - */ - public SecurityManager(Broker broker, boolean managementMode) + public SecurityManager(Broker<?> broker, boolean managementMode) { _managementMode = managementMode; _broker = broker; } - /* - * Used by the VirtualHost to allow deferring to the broker level security plugins if required. - */ - public SecurityManager(SecurityManager parent, String aclFile, String vhostName) - { - _managementMode = parent._managementMode; - _broker = parent._broker; - if(!_managementMode) - { - configureVirtualHostAclPlugin(aclFile, vhostName); - - // our global plugins are the parent's host plugins - _globalPlugins = parent._hostPlugins; - } - } - public static Subject getSubjectWithAddedSystemRights() { Subject subject = Subject.getSubject(AccessController.getContext()); @@ -135,50 +108,11 @@ public class SecurityManager implements ConfigurationChangeListener return subject; } - private void configureVirtualHostAclPlugin(String aclFile, String vhostName) - { - if(aclFile != null) - { - Map<String, Object> attributes = new HashMap<String, Object>(); - - attributes.put(AccessControlProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); - attributes.put(FileAccessControlProviderConstants.PATH, aclFile); - - for (AccessControlFactory provider : (new QpidServiceLoader<AccessControlFactory>()).instancesOf(AccessControlFactory.class)) - { - AccessControl accessControl = provider.createInstance(attributes, _broker); - accessControl.open(); - if(accessControl != null) - { - String pluginTypeName = getPluginTypeName(accessControl); - _hostPlugins.put(pluginTypeName, accessControl); - - if(_logger.isDebugEnabled()) - { - _logger.debug("Added access control to host plugins with name: " + vhostName); - } - - break; - } - } - } - - if(_logger.isDebugEnabled()) - { - _logger.debug("Configured " + _hostPlugins.size() + " access control plugins"); - } - } - private String getPluginTypeName(AccessControl accessControl) { return accessControl.getClass().getName(); } - public static Logger getLogger() - { - return _logger; - } - public static boolean isSystemProcess() { Subject subject = Subject.getSubject(AccessController.getContext()); @@ -234,71 +168,14 @@ public class SecurityManager implements ConfigurationChangeListener return true; } - Map<String, AccessControl> remainingPlugins = _globalPlugins.isEmpty() - ? Collections.<String, AccessControl>emptyMap() - : _hostPlugins.isEmpty() ? _globalPlugins : new HashMap<String, AccessControl>(_globalPlugins); - - if(!_hostPlugins.isEmpty()) - { - for (Entry<String, AccessControl> hostEntry : _hostPlugins.entrySet()) - { - // Create set of global only plugins - AccessControl globalPlugin = remainingPlugins.get(hostEntry.getKey()); - if (globalPlugin != null) - { - remainingPlugins.remove(hostEntry.getKey()); - } - - Result host = checker.allowed(hostEntry.getValue()); - - if (host == Result.DENIED) - { - // Something vetoed the access, we're done - return false; - } - - // host allow overrides global allow, so only check global on abstain or defer - if (host != Result.ALLOWED) - { - if (globalPlugin == null) - { - if (host == Result.DEFER) - { - host = hostEntry.getValue().getDefault(); - } - if (host == Result.DENIED) - { - return false; - } - } - else - { - Result global = checker.allowed(globalPlugin); - if (global == Result.DEFER) - { - global = globalPlugin.getDefault(); - } - if (global == Result.ABSTAIN && host == Result.DEFER) - { - global = hostEntry.getValue().getDefault(); - } - if (global == Result.DENIED) - { - return false; - } - } - } - } - } - - for (AccessControl plugin : remainingPlugins.values()) + for (AccessControl plugin : _plugins.values()) { Result remaining = checker.allowed(plugin); - if (remaining == Result.DEFER) + if (remaining == Result.DEFER) { remaining = plugin.getDefault(); } - if (remaining == Result.DENIED) + if (remaining == Result.DENIED) { return false; } @@ -308,28 +185,23 @@ public class SecurityManager implements ConfigurationChangeListener return true; } - public void authoriseCreateBinding(BindingImpl binding) + public void authoriseCreateBinding(final BindingImpl binding) { - final ExchangeImpl exch = binding.getExchange(); - final AMQQueue queue = binding.getAMQQueue(); - final String bindingKey = binding.getBindingKey(); - - boolean allowed = - checkAllPlugins(new AccessCheck() + boolean allowed = checkAllPlugins(new AccessCheck() { Result allowed(AccessControl plugin) { - return plugin.authorise(BIND, EXCHANGE, new ObjectProperties(exch, queue, bindingKey)); + return plugin.authorise(BIND, EXCHANGE, new ObjectProperties(binding)); } }); if(!allowed) { - throw new AccessControlException("Permission denied: binding " + bindingKey); + throw new AccessControlException("Permission denied: binding " + binding.getBindingKey()); } } - public void authoriseMethod(final Operation operation, final String componentName, final String methodName) + public void authoriseMethod(final Operation operation, final String componentName, final String methodName, final String virtualHostName) { boolean allowed = checkAllPlugins(new AccessCheck() { @@ -339,8 +211,11 @@ public class SecurityManager implements ConfigurationChangeListener properties.setName(methodName); if (componentName != null) { - // Only set the property if there is a component name - properties.put(ObjectProperties.Property.COMPONENT, componentName); + properties.put(ObjectProperties.Property.COMPONENT, componentName); + } + if (virtualHostName != null) + { + properties.put(ObjectProperties.Property.VIRTUALHOST_NAME, virtualHostName); } return plugin.authorise(operation, METHOD, properties); } @@ -367,15 +242,19 @@ public class SecurityManager implements ConfigurationChangeListener public void authoriseCreateConnection(final AMQConnectionModel connection) { + final String virtualHostName = connection.getVirtualHostName(); if(!checkAllPlugins(new AccessCheck() { Result allowed(AccessControl plugin) { - return plugin.authorise(Operation.ACCESS, VIRTUALHOST, ObjectProperties.EMPTY); + // We put the name into the properties under both name and virtualhost_name so the user may express predicates using either. + ObjectProperties properties = new ObjectProperties(virtualHostName); + properties.put(Property.VIRTUALHOST_NAME, virtualHostName); + return plugin.authorise(Operation.ACCESS, VIRTUALHOST, properties); } })) { - throw new AccessControlException("Permission denied: " + connection.getVirtualHostName()); + throw new AccessControlException("Permission denied: " + virtualHostName); } } @@ -403,10 +282,7 @@ public class SecurityManager implements ConfigurationChangeListener { Result allowed(AccessControl plugin) { - return plugin.authorise(CREATE, EXCHANGE, new ObjectProperties(exchange.isAutoDelete(), - exchange.isDurable(), - exchangeName, - exchange.getTypeName())); + return plugin.authorise(CREATE, EXCHANGE, new ObjectProperties(exchange)); } })) { @@ -421,11 +297,7 @@ public class SecurityManager implements ConfigurationChangeListener { Result allowed(AccessControl plugin) { - return plugin.authorise(CREATE, QUEUE, new ObjectProperties(queue.getAttribute(Queue.LIFETIME_POLICY) != LifetimePolicy.PERMANENT, - Boolean.TRUE.equals(queue.getAttribute(Queue.DURABLE)), - queue.getAttribute(Queue.EXCLUSIVE) != ExclusivityPolicy.NONE, - queueName, - queue.getOwner())); + return plugin.authorise(CREATE, QUEUE, new ObjectProperties(queue)); } })) { @@ -470,7 +342,7 @@ public class SecurityManager implements ConfigurationChangeListener { Result allowed(AccessControl plugin) { - return plugin.authorise(UPDATE, EXCHANGE, new ObjectProperties(exchange.getName())); + return plugin.authorise(UPDATE, EXCHANGE, new ObjectProperties(exchange)); } })) { @@ -484,7 +356,7 @@ public class SecurityManager implements ConfigurationChangeListener { Result allowed(AccessControl plugin) { - return plugin.authorise(DELETE, EXCHANGE, new ObjectProperties(exchange.getName())); + return plugin.authorise(DELETE, EXCHANGE, new ObjectProperties(exchange)); } })) { @@ -522,39 +394,15 @@ public class SecurityManager implements ConfigurationChangeListener } } - private ConcurrentHashMap<String, ConcurrentHashMap<String, PublishAccessCheck>> _immediatePublishPropsCache - = new ConcurrentHashMap<String, ConcurrentHashMap<String, PublishAccessCheck>>(); - private ConcurrentHashMap<String, ConcurrentHashMap<String, PublishAccessCheck>> _publishPropsCache - = new ConcurrentHashMap<String, ConcurrentHashMap<String, PublishAccessCheck>>(); - - public void authorisePublish(final boolean immediate, String routingKey, String exchangeName) + public void authorisePublish(final boolean immediate, String routingKey, String exchangeName, String virtualHostName) { - if(routingKey == null) + PublishAccessCheckCacheEntry key = new PublishAccessCheckCacheEntry(immediate, routingKey, exchangeName, virtualHostName); + PublishAccessCheck check = _publishAccessCheckCache.get(key); + if (check == null) { - routingKey = ""; + check = new PublishAccessCheck(new ObjectProperties(virtualHostName, exchangeName, routingKey, immediate)); + _publishAccessCheckCache.putIfAbsent(key, check); } - if(exchangeName == null) - { - exchangeName = ""; - } - PublishAccessCheck check; - ConcurrentHashMap<String, ConcurrentHashMap<String, PublishAccessCheck>> cache = - immediate ? _immediatePublishPropsCache : _publishPropsCache; - - ConcurrentHashMap<String, PublishAccessCheck> exchangeMap = cache.get(exchangeName); - if(exchangeMap == null) - { - cache.putIfAbsent(exchangeName, new ConcurrentHashMap<String, PublishAccessCheck>()); - exchangeMap = cache.get(exchangeName); - } - - check = exchangeMap.get(routingKey); - if(check == null) - { - check = new PublishAccessCheck(new ObjectProperties(exchangeName, routingKey, immediate)); - exchangeMap.put(routingKey, check); - } - if(!checkAllPlugins(check)) { throw new AccessControlException("Permission denied, publish to: exchange-name '" + exchangeName + "'"); @@ -575,17 +423,17 @@ public class SecurityManager implements ConfigurationChangeListener } } - public void authoriseUnbind(final ExchangeImpl exch, final String routingKey, final AMQQueue queue) + public void authoriseUnbind(final BindingImpl binding) { if(! checkAllPlugins(new AccessCheck() { Result allowed(AccessControl plugin) { - return plugin.authorise(UNBIND, EXCHANGE, new ObjectProperties(exch, queue, routingKey)); + return plugin.authorise(UNBIND, EXCHANGE, new ObjectProperties(binding)); } })) { - throw new AccessControlException("Permission denied: unbinding " + routingKey); + throw new AccessControlException("Permission denied: unbinding " + binding.getBindingKey()); } } @@ -618,29 +466,29 @@ public class SecurityManager implements ConfigurationChangeListener { if(newState == State.ACTIVE) { - synchronized (_hostPlugins) + synchronized (_plugins) { AccessControl accessControl = ((AccessControlProvider)object).getAccessControl(); String pluginTypeName = getPluginTypeName(accessControl); - _hostPlugins.put(pluginTypeName, accessControl); + _plugins.put(pluginTypeName, accessControl); } } else if(newState == State.DELETED) { - synchronized (_hostPlugins) + synchronized (_plugins) { AccessControl control = ((AccessControlProvider)object).getAccessControl(); String pluginTypeName = getPluginTypeName(control); // Remove the type->control mapping for this type key only if the // given control is actually referred to. - if(_hostPlugins.containsValue(control)) + if(_plugins.containsValue(control)) { // If we are removing this control, check if another of the same // type already exists on the broker and use it in instead. AccessControl other = null; - Collection<AccessControlProvider> providers = _broker.getAccessControlProviders(); + Collection<AccessControlProvider<?>> providers = _broker.getAccessControlProviders(); for(AccessControlProvider p : providers) { if(p == object || p.getState() != State.ACTIVE) @@ -660,12 +508,12 @@ public class SecurityManager implements ConfigurationChangeListener if(other != null) { //Another control of this type was found, use it instead - _hostPlugins.replace(pluginTypeName, control, other); + _plugins.replace(pluginTypeName, control, other); } else { //No other was found, remove the type entirely - _hostPlugins.remove(pluginTypeName); + _plugins.remove(pluginTypeName); } } } @@ -718,4 +566,90 @@ public class SecurityManager implements ConfigurationChangeListener }); } + public static class PublishAccessCheckCacheEntry + { + private final boolean _immediate; + private final String _routingKey; + private final String _exchangeName; + private final String _virtualHostName; + + public PublishAccessCheckCacheEntry(boolean immediate, String routingKey, String exchangeName, String virtualHostName) + { + super(); + _immediate = immediate; + _routingKey = routingKey; + _exchangeName = exchangeName; + _virtualHostName = virtualHostName; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((_exchangeName == null) ? 0 : _exchangeName.hashCode()); + result = prime * result + (_immediate ? 1231 : 1237); + result = prime * result + ((_routingKey == null) ? 0 : _routingKey.hashCode()); + result = prime * result + ((_virtualHostName == null) ? 0 : _virtualHostName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + PublishAccessCheckCacheEntry other = (PublishAccessCheckCacheEntry) obj; + if (_exchangeName == null) + { + if (other._exchangeName != null) + { + return false; + } + } + else if (!_exchangeName.equals(other._exchangeName)) + { + return false; + } + if (_immediate != other._immediate) + { + return false; + } + if (_routingKey == null) + { + if (other._routingKey != null) + { + return false; + } + } + else if (!_routingKey.equals(other._routingKey)) + { + return false; + } + if (_virtualHostName == null) + { + if (other._virtualHostName != null) + { + return false; + } + } + else if (!_virtualHostName.equals(other._virtualHostName)) + { + return false; + } + return true; + } + + + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java index ae0241314f..6122cef5c1 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/ObjectProperties.java @@ -26,8 +26,10 @@ import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.qpid.server.binding.BindingImpl; import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.queue.AMQQueue; /** @@ -39,7 +41,7 @@ import org.apache.qpid.server.queue.AMQQueue; */ public class ObjectProperties { - public static final String STAR= "*"; + public static final String WILD_CARD = "*"; public static final ObjectProperties EMPTY = new ObjectProperties(); @@ -65,7 +67,8 @@ public class ObjectProperties PACKAGE, CLASS, FROM_NETWORK, - FROM_HOSTNAME; + FROM_HOSTNAME, + VIRTUALHOST_NAME; private static final Map<String, Property> _canonicalNameToPropertyMap = new HashMap<String, ObjectProperties.Property>(); @@ -152,60 +155,49 @@ public class ObjectProperties { put(Property.OWNER, queue.getOwner()); } - + put(Property.VIRTUALHOST_NAME, queue.getParent(VirtualHost.class).getName()); } - public ObjectProperties(ExchangeImpl exch, AMQQueue queue, String routingKey) + public ObjectProperties(BindingImpl binding) { - this(queue); + ExchangeImpl<?> exch = binding.getExchange(); + AMQQueue<?> queue = binding.getAMQQueue(); + String routingKey = binding.getBindingKey(); setName(exch.getName()); - put(Property.QUEUE_NAME, queue.getName()); + put(Property.QUEUE_NAME, queue.getName()); put(Property.ROUTING_KEY, routingKey); - } - - public ObjectProperties(String exchangeName, String routingKey, Boolean immediate) - { - this(exchangeName, routingKey); + put(Property.VIRTUALHOST_NAME, queue.getParent(VirtualHost.class).getName()); - put(Property.IMMEDIATE, immediate); + // The temporary attribute (inherited from the binding's queue) seems to exist to allow the user to + // express rules about the binding of temporary queues (whose names cannot be predicted). + put(Property.TEMPORARY, queue.getLifetimePolicy() != LifetimePolicy.PERMANENT); + put(Property.DURABLE, queue.isDurable()); } - public ObjectProperties(String exchangeName, String routingKey) + public ObjectProperties(String virtualHostName, String exchangeName, String routingKey, Boolean immediate) { super(); setName(exchangeName); put(Property.ROUTING_KEY, routingKey); + put(Property.IMMEDIATE, immediate); + put(Property.VIRTUALHOST_NAME, virtualHostName); } - public ObjectProperties(Boolean autoDelete, Boolean durable, String exchangeName, - String exchangeType) - { - super(); - - setName(exchangeName); - - put(Property.AUTO_DELETE, autoDelete); - put(Property.TEMPORARY, autoDelete); - put(Property.DURABLE, durable); - put(Property.TYPE, exchangeType); - } - - public ObjectProperties(Boolean autoDelete, Boolean durable, Boolean exclusive, - String queueName, String owner) + public ObjectProperties(ExchangeImpl<?> exchange) { super(); - setName(queueName); + setName(exchange.getName()); - put(Property.AUTO_DELETE, autoDelete); - put(Property.TEMPORARY, autoDelete); - put(Property.DURABLE, durable); - put(Property.EXCLUSIVE, exclusive); - put(Property.OWNER, owner); + put(Property.AUTO_DELETE, exchange.isAutoDelete()); + put(Property.TEMPORARY, exchange.getLifetimePolicy() != LifetimePolicy.PERMANENT); + put(Property.DURABLE, exchange.isDurable()); + put(Property.TYPE, exchange.getTypeName()); + put(Property.VIRTUALHOST_NAME, exchange.getParent(VirtualHost.class).getName()); } public ObjectProperties(Boolean exclusive, Boolean noAck, Boolean noLocal, Boolean nowait, AMQQueue queue) @@ -283,8 +275,8 @@ public class ObjectProperties { return (StringUtils.isEmpty(ruleValue) || StringUtils.equals(thisValue, ruleValue)) - || ruleValue.equals(STAR) - || (ruleValue.endsWith(STAR) + || ruleValue.equals(WILD_CARD) + || (ruleValue.endsWith(WILD_CARD) && thisValue != null && thisValue.length() >= ruleValue.length() - 1 && thisValue.startsWith(ruleValue.substring(0, ruleValue.length() - 1))); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java index a683199abc..f36695cb86 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/access/OperationLoggingDetails.java @@ -32,6 +32,42 @@ public class OperationLoggingDetails extends ObjectProperties } @Override + public int hashCode() + { + return super.hashCode() + ((_description == null) ? 0 : _description.hashCode()); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (!super.equals(obj)) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + OperationLoggingDetails other = (OperationLoggingDetails) obj; + if (_description == null) + { + if (other._description != null) + { + return false; + } + } + else if (!_description.equals(other._description)) + { + return false; + } + return true; + } + + @Override public String toString() { StringBuilder sb = new StringBuilder("("); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java index 17c9a19e50..2274ddb189 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/AbstractAuthenticationManager.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.security.auth.manager; import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.model.*; import org.apache.qpid.server.model.adapter.AbstractConfiguredObject; @@ -85,8 +86,11 @@ public abstract class AbstractAuthenticationManager<T extends AbstractAuthentica _preferencesProvider = preferencesProvider; } - - + @Override + public void recoverUser(final User user) + { + throw new IllegalConfigurationException("Cannot associate " + user + " with authentication provider " + this); + } @Override public String setName(final String currentName, final String desiredName) diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManager.java new file mode 100644 index 0000000000..097d0bfb9d --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManager.java @@ -0,0 +1,696 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.configuration.ConfigurationEntry; +import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; +import org.apache.qpid.server.configuration.RecovererProvider; +import org.apache.qpid.server.configuration.updater.ChangeAttributesTask; +import org.apache.qpid.server.configuration.updater.TaskExecutor; +import org.apache.qpid.server.model.*; +import org.apache.qpid.server.model.adapter.AbstractConfiguredObject; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.UsernamePrincipal; +import org.apache.qpid.server.security.auth.sasl.scram.ScramSHA1SaslServer; + +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 javax.xml.bind.DatatypeConverter; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.security.AccessControlException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class ScramSHA1AuthenticationManager + extends AbstractAuthenticationManager<ScramSHA1AuthenticationManager> + implements PasswordCredentialManagingAuthenticationProvider<ScramSHA1AuthenticationManager>, + RecovererProvider +{ + public static final String SCRAM_USER_TYPE = "scram"; + private static final Charset ASCII = Charset.forName("ASCII"); + public static final String HMAC_SHA_1 = "HmacSHA1"; + private final SecureRandom _random = new SecureRandom(); + private int _iterationCount = 4096; + private Map<String, ScramAuthUser> _users = new ConcurrentHashMap<String, ScramAuthUser>(); + + + protected ScramSHA1AuthenticationManager(final Broker broker, + final Map<String, Object> defaults, + final Map<String, Object> attributes, + final boolean recovering) + { + super(broker, defaults, attributes); + } + + @Override + public void initialise() + { + + } + + @Override + public String getMechanisms() + { + return ScramSHA1SaslServer.MECHANISM; + } + + @Override + public SaslServer createSaslServer(final String mechanism, + final String localFQDN, + final Principal externalPrincipal) + throws SaslException + { + return new ScramSHA1SaslServer(this); + } + + @Override + public AuthenticationResult authenticate(final SaslServer server, final byte[] response) + { + try + { + // Process response from the client + byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]); + + if (server.isComplete()) + { + final String userId = server.getAuthorizationID(); + return new AuthenticationResult(new UsernamePrincipal(userId)); + } + else + { + return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE); + } + } + catch (SaslException e) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR, e); + } + } + + @Override + public AuthenticationResult authenticate(final String username, final String password) + { + ScramAuthUser user = getUser(username); + if(user != null) + { + final String[] usernamePassword = user.getPassword().split(","); + byte[] salt = DatatypeConverter.parseBase64Binary(usernamePassword[0]); + try + { + if(Arrays.equals(DatatypeConverter.parseBase64Binary(usernamePassword[1]),createSaltedPassword(salt, password))) + { + return new AuthenticationResult(new UsernamePrincipal(username)); + } + } + catch (SaslException e) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR,e); + } + + } + + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR); + + + } + + @Override + public void delete() + { + + } + + @Override + public void close() + { + + } + + public int getIterationCount() + { + return _iterationCount; + } + + public byte[] getSalt(final String username) + { + ScramAuthUser user = getUser(username); + + if(user == null) + { + // don't disclose that the user doesn't exist, just generate random data so the failure is indistinguishable + // from the "wrong password" case + + byte[] salt = new byte[32]; + _random.nextBytes(salt); + return salt; + } + else + { + return DatatypeConverter.parseBase64Binary(user.getPassword().split(",")[0]); + } + } + + private static final byte[] INT_1 = new byte[]{0, 0, 0, 1}; + + public byte[] getSaltedPassword(final String username) throws SaslException + { + ScramAuthUser user = getUser(username); + if(user == null) + { + throw new SaslException("Authentication Failed"); + } + else + { + return DatatypeConverter.parseBase64Binary(user.getPassword().split(",")[1]); + } + } + + private ScramAuthUser getUser(final String username) + { + return _users.get(username); + } + + private byte[] createSaltedPassword(byte[] salt, String password) throws SaslException + { + Mac mac = createSha1Hmac(password.getBytes(ASCII)); + + mac.update(salt); + mac.update(INT_1); + byte[] result = mac.doFinal(); + + byte[] previous = null; + for(int i = 1; i < getIterationCount(); i++) + { + mac.update(previous != null? previous: result); + previous = mac.doFinal(); + for(int x = 0; x < result.length; x++) + { + result[x] ^= previous[x]; + } + } + + return result; + + } + + private Mac createSha1Hmac(final byte[] keyBytes) + throws SaslException + { + try + { + SecretKeySpec key = new SecretKeySpec(keyBytes, HMAC_SHA_1); + Mac mac = Mac.getInstance(HMAC_SHA_1); + mac.init(key); + return mac; + } + catch (NoSuchAlgorithmException e) + { + throw new SaslException(e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new SaslException(e.getMessage(), e); + } + } + + @Override + public boolean createUser(final String username, final String password, final Map<String, String> attributes) + { + if (getTaskExecutor().isTaskExecutorThread()) + { + getSecurityManager().authoriseUserOperation(Operation.CREATE, username); + if(_users.containsKey(username)) + { + throw new IllegalArgumentException("User '"+username+"' already exists"); + } + try + { + Map<String,Object> userAttrs = new HashMap<String, Object>(); + userAttrs.put(User.ID, UUID.randomUUID()); + userAttrs.put(User.NAME, username); + userAttrs.put(User.PASSWORD, createStoredPassword(password)); + userAttrs.put(User.TYPE, SCRAM_USER_TYPE); + ScramAuthUser user = new ScramAuthUser(userAttrs); + _users.put(username, user); + + return true; + } + catch (SaslException e) + { + throw new IllegalArgumentException(e); + } + } + else + { + return getTaskExecutor().submitAndWait(new TaskExecutor.Task<Boolean>() + { + @Override + public Boolean call() + { + return createUser(username, password, attributes); + } + }); + } + + } + + private SecurityManager getSecurityManager() + { + return getBroker().getSecurityManager(); + } + + @Override + public void deleteUser(final String user) throws AccountNotFoundException + { + if (getTaskExecutor().isTaskExecutorThread()) + { + + final ScramAuthUser authUser = getUser(user); + if(authUser != null) + { + authUser.setState(State.ACTIVE, State.DELETED); + } + else + { + throw new AccountNotFoundException("No such user: '" + user + "'"); + } + } + else + { + AccountNotFoundException e = + getTaskExecutor().submitAndWait(new TaskExecutor.Task<AccountNotFoundException>() { + + @Override + public AccountNotFoundException call() + { + try + { + deleteUser(user); + return null; + } + catch (AccountNotFoundException e) + { + return e; + } + + } + }); + + if(e != null) + { + throw e; + } + } + } + + @Override + public void setPassword(final String username, final String password) throws AccountNotFoundException + { + if (getTaskExecutor().isTaskExecutorThread()) + { + final ScramAuthUser authUser = getUser(username); + if(authUser != null) + { + authUser.setPassword(password); + } + else + { + throw new AccountNotFoundException("No such user: '" + username + "'"); + } + } + else + { + AccountNotFoundException e = + getTaskExecutor().submitAndWait(new TaskExecutor.Task<AccountNotFoundException>() + { + + @Override + public AccountNotFoundException call() + { + try + { + setPassword(username, password); + return null; + } + catch (AccountNotFoundException e) + { + return e; + } + + } + }); + + if (e != null) + { + throw e; + } + } + + } + + @Override + public Map<String, Map<String, String>> getUsers() + { + if (getTaskExecutor().isTaskExecutorThread()) + { + Map<String, Map<String,String>> users = new HashMap<String, Map<String, String>>(); + for(String user : _users.keySet()) + { + users.put(user, Collections.<String,String>emptyMap()); + } + return users; + } + else + { + return getTaskExecutor().submitAndWait(new TaskExecutor.Task<Map<String, Map<String, String>>>() + { + @Override + public Map<String, Map<String, String>> call() + { + return getUsers(); + } + }); + } + } + + @Override + public void reload() throws IOException + { + + } + + private static Map<Class<? extends ConfiguredObject>, ConfiguredObject<?>> parentsMap(final ScramSHA1AuthenticationManager scramSHA1AuthenticationManager) + { + + final Map<Class<? extends ConfiguredObject>, ConfiguredObject<?>> map = new HashMap<Class<? extends ConfiguredObject>, ConfiguredObject<?>>(); + map.put(AuthenticationProvider.class, scramSHA1AuthenticationManager); + return map; + } + + @Override + public ConfiguredObjectRecoverer<? extends ConfiguredObject> getRecoverer(final String type) + { + if("User".equals(type)) + { + return new UserRecoverer(); + } + else + { + return null; + } + } + + private class ScramAuthUser extends AbstractConfiguredObject<ScramAuthUser> implements User<ScramAuthUser> + { + + + protected ScramAuthUser(final Map<String, Object> attributes) + { + super(parentsMap(ScramSHA1AuthenticationManager.this), + Collections.<String,Object>emptyMap(), + attributes, ScramSHA1AuthenticationManager.this.getTaskExecutor()); + + if(!ASCII.newEncoder().canEncode(getName())) + { + throw new IllegalArgumentException("Scram SHA1 user names are restricted to characters in the ASCII charset"); + } + } + + @Override + protected boolean setState(final State currentState, final State desiredState) + { + if(desiredState == State.DELETED) + { + getSecurityManager().authoriseUserOperation(Operation.DELETE, getName()); + _users.remove(getName()); + ScramSHA1AuthenticationManager.this.childRemoved(this); + return true; + } + else + { + return false; + } + } + + @Override + public void setAttributes(final Map<String, Object> attributes) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + if (getTaskExecutor().isTaskExecutorThread()) + { + Map<String,Object> modifiedAttributes = new HashMap<String, Object>(attributes); + final String newPassword = (String) attributes.get(User.PASSWORD); + if(attributes.containsKey(User.PASSWORD) && !newPassword.equals(getActualAttributes().get(User.PASSWORD))) + { + try + { + modifiedAttributes.put(User.PASSWORD, createStoredPassword(newPassword)); + } + catch (SaslException e) + { + throw new IllegalArgumentException(e); + } + } + super.setAttributes(modifiedAttributes); + } + else + { + getTaskExecutor().submitAndWait(new ChangeAttributesTask(this, attributes)); + } + + } + + @Override + public Object getAttribute(final String name) + { + if(PASSWORD.equals(name)) + { + return null; // for security reasons we don't expose the password + } + return super.getAttribute(name); + } + + @Override + public String getPassword() + { + return (String) getActualAttributes().get(PASSWORD); + } + + @Override + public void setPassword(final String password) + { + getSecurityManager().authoriseUserOperation(Operation.UPDATE, getName()); + + try + { + changeAttribute(User.PASSWORD, getAttribute(User.PASSWORD), createStoredPassword(password)); + } + catch (SaslException e) + { + throw new IllegalArgumentException(e); + } + } + + @Override + public String setName(final String currentName, final String desiredName) + throws IllegalStateException, AccessControlException + { + throw new IllegalStateException("Names cannot be updated"); + } + + @Override + public State getState() + { + return State.ACTIVE; + } + + @Override + public boolean isDurable() + { + return true; + } + + @Override + public void setDurable(final boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + @Override + public LifetimePolicy setLifetimePolicy(final LifetimePolicy expected, final LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + if(expected == desired && expected == LifetimePolicy.PERMANENT) + { + return LifetimePolicy.PERMANENT; + } + throw new IllegalArgumentException("Cannot change lifetime policy of a user"); + + } + + @Override + public <C extends ConfiguredObject> Collection<C> getChildren(final Class<C> clazz) + { + return Collections.emptySet(); + } + + @Override + public Map<String, Object> getPreferences() + { + PreferencesProvider preferencesProvider = getPreferencesProvider(); + if (preferencesProvider == null) + { + return null; + } + return preferencesProvider.getPreferences(this.getName()); + } + + @Override + public Object getPreference(String name) + { + Map<String, Object> preferences = getPreferences(); + if (preferences == null) + { + return null; + } + return preferences.get(name); + } + + @Override + public Map<String, Object> setPreferences(Map<String, Object> preferences) + { + PreferencesProvider preferencesProvider = getPreferencesProvider(); + if (preferencesProvider == null) + { + return null; + } + return preferencesProvider.setPreferences(this.getName(), preferences); + } + + @Override + public boolean deletePreferences() + { + PreferencesProvider preferencesProvider = getPreferencesProvider(); + if (preferencesProvider == null) + { + return false; + } + String[] deleted = preferencesProvider.deletePreferences(this.getName()); + return deleted.length == 1; + } + + @Override + public Collection<String> getAttributeNames() + { + return getAttributeNames(getClass()); + } + } + + @Override + public void recoverUser(final User user) + { + _users.put(user.getName(), (ScramAuthUser) user); + } + + protected String createStoredPassword(final String password) throws SaslException + { + byte[] salt = new byte[32]; + _random.nextBytes(salt); + byte[] passwordBytes = createSaltedPassword(salt, password); + return DatatypeConverter.printBase64Binary(salt) + "," + DatatypeConverter.printBase64Binary(passwordBytes); + } + + @Override + public <C extends ConfiguredObject> C addChild(final Class<C> childClass, + final Map<String, Object> attributes, + final ConfiguredObject... otherParents) + { + if(childClass == User.class) + { + String username = (String) attributes.get("name"); + String password = (String) attributes.get("password"); + Principal p = new UsernamePrincipal(username); + + if(createUser(username, password,null)) + { + @SuppressWarnings("unchecked") + C principalAdapter = (C) _users.get(username); + return principalAdapter; + } + else + { + return null; + + } + } + return super.addChild(childClass, attributes, otherParents); + } + + public <C extends ConfiguredObject> Collection<C> getChildren(Class<C> clazz) + { + if(clazz == User.class) + { + return new ArrayList(_users.values()); + } + else + { + return super.getChildren(clazz); + } + } + + private class UserRecoverer implements ConfiguredObjectRecoverer<ScramAuthUser> + { + @Override + public ScramAuthUser create(final RecovererProvider recovererProvider, + final ConfigurationEntry entry, + final ConfiguredObject... parents) + { + + Map<String,Object> attributes = new HashMap<String, Object>(entry.getAttributes()); + attributes.put(User.ID,entry.getId()); + return new ScramAuthUser(attributes); + } + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerFactory.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerFactory.java new file mode 100644 index 0000000000..dd6f77e474 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerFactory.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.manager; + +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.plugin.AuthenticationManagerFactory; +import org.apache.qpid.server.util.ResourceBundleLoader; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public class ScramSHA1AuthenticationManagerFactory implements AuthenticationManagerFactory +{ + + public static final String PROVIDER_TYPE = "SCRAM-SHA1"; + + public static final String ATTRIBUTE_NAME = "name"; + + public static final Collection<String> ATTRIBUTES = Collections.<String> unmodifiableList(Arrays.asList( + AuthenticationProvider.TYPE + )); + + @Override + public ScramSHA1AuthenticationManager createInstance(Broker broker, + Map<String, Object> attributes, + final boolean recovering) + { + if (attributes == null || !PROVIDER_TYPE.equals(attributes.get(AuthenticationProvider.TYPE))) + { + return null; + } + + + return new ScramSHA1AuthenticationManager(broker, Collections.<String,Object>emptyMap(),attributes, false); + } + + @Override + public Collection<String> getAttributeNames() + { + return ATTRIBUTES; + } + + @Override + public String getType() + { + return PROVIDER_TYPE; + } + + @Override + public Map<String, String> getAttributeDescriptions() + { + return Collections.emptyMap(); + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/sasl/scram/ScramSHA1SaslServer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/sasl/scram/ScramSHA1SaslServer.java new file mode 100644 index 0000000000..71ef386e3e --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/security/auth/sasl/scram/ScramSHA1SaslServer.java @@ -0,0 +1,273 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.auth.sasl.scram; + +import org.apache.qpid.server.security.auth.manager.ScramSHA1AuthenticationManager; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; +import javax.xml.bind.DatatypeConverter; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.UUID; + +public class ScramSHA1SaslServer implements SaslServer +{ + public static final String MECHANISM = "SCRAM-SHA-1"; + + private static final Charset ASCII = Charset.forName("ASCII"); + + private final ScramSHA1AuthenticationManager _authManager; + private State _state = State.INITIAL; + private String _nonce; + private String _username; + private byte[] _gs2Header; + private String _serverFirstMessage; + private String _clientFirstMessageBare; + private byte[] _serverSignature; + + public ScramSHA1SaslServer(final ScramSHA1AuthenticationManager authenticationManager) + { + _authManager = authenticationManager; + } + + enum State + { + INITIAL, + SERVER_FIRST_MESSAGE_SENT, + COMPLETE + } + + @Override + public String getMechanismName() + { + return MECHANISM; + } + + @Override + public byte[] evaluateResponse(final byte[] response) throws SaslException + { + byte[] challenge; + switch (_state) + { + case INITIAL: + challenge = generateServerFirstMessage(response); + _state = State.SERVER_FIRST_MESSAGE_SENT; + break; + case SERVER_FIRST_MESSAGE_SENT: + challenge = generateServerFinalMessage(response); + _state = State.COMPLETE; + break; + default: + throw new SaslException("No response expected in state " + _state); + + } + return challenge; + } + + private byte[] generateServerFirstMessage(final byte[] response) throws SaslException + { + String clientFirstMessage = new String(response, ASCII); + if(!clientFirstMessage.startsWith("n")) + { + throw new SaslException("Cannot parse gs2-header"); + } + String[] parts = clientFirstMessage.split(","); + if(parts.length < 4) + { + throw new SaslException("Cannot parse client first message"); + } + _gs2Header = ("n,"+parts[1]+",").getBytes(ASCII); + _clientFirstMessageBare = clientFirstMessage.substring(_gs2Header.length); + if(!parts[2].startsWith("n=")) + { + throw new SaslException("Cannot parse client first message"); + } + _username = decodeUsername(parts[2].substring(2)); + if(!parts[3].startsWith("r=")) + { + throw new SaslException("Cannot parse client first message"); + } + _nonce = parts[3].substring(2) + UUID.randomUUID().toString(); + + int count = _authManager.getIterationCount(); + byte[] saltBytes = _authManager.getSalt(_username); + _serverFirstMessage = "r="+_nonce+",s="+ DatatypeConverter.printBase64Binary(saltBytes)+",i=" + count; + return _serverFirstMessage.getBytes(ASCII); + } + + private String decodeUsername(String username) throws SaslException + { + if(username.contains("=")) + { + String check = username; + while (check.contains("=")) + { + check = check.substring(check.indexOf('=') + 1); + if (!(check.startsWith("2C") || check.startsWith("3D"))) + { + throw new SaslException("Invalid username"); + } + } + username = username.replace("=2C", ","); + username = username.replace("=3D","="); + } + return username; + } + + + private byte[] generateServerFinalMessage(final byte[] response) throws SaslException + { + try + { + String clientFinalMessage = new String(response, ASCII); + String[] parts = clientFinalMessage.split(","); + if(!parts[0].startsWith("c=")) + { + throw new SaslException("Cannot parse client final message"); + } + if(!Arrays.equals(_gs2Header,DatatypeConverter.parseBase64Binary(parts[0].substring(2)))) + { + throw new SaslException("Client final message channel bind data invalid"); + } + if(!parts[1].startsWith("r=")) + { + throw new SaslException("Cannot parse client final message"); + } + if(!parts[1].substring(2).equals(_nonce)) + { + throw new SaslException("Client final message has incorrect nonce value"); + } + if(!parts[parts.length-1].startsWith("p=")) + { + throw new SaslException("Client final message does not have proof"); + } + + String clientFinalMessageWithoutProof = clientFinalMessage.substring(0,clientFinalMessage.length()-(1+parts[parts.length-1].length())); + byte[] proofBytes = DatatypeConverter.parseBase64Binary(parts[parts.length-1].substring(2)); + + String authMessage = _clientFirstMessageBare + "," + _serverFirstMessage + "," + clientFinalMessageWithoutProof; + + byte[] saltedPassword = _authManager.getSaltedPassword(_username); + + byte[] clientKey = computeHmacSHA1(saltedPassword, "Client Key"); + + byte[] storedKey = MessageDigest.getInstance("SHA1").digest(clientKey); + + byte[] clientSignature = computeHmacSHA1(storedKey, authMessage); + + byte[] clientProof = clientKey.clone(); + for(int i = 0 ; i < clientProof.length; i++) + { + clientProof[i] ^= clientSignature[i]; + } + + if(!Arrays.equals(clientProof, proofBytes)) + { + throw new SaslException("Authentication failed"); + } + byte[] serverKey = computeHmacSHA1(saltedPassword, "Server Key"); + String finalResponse = "v=" + DatatypeConverter.printBase64Binary(computeHmacSHA1(serverKey, authMessage)); + + return finalResponse.getBytes(ASCII); + } + catch (NoSuchAlgorithmException e) + { + throw new SaslException(e.getMessage(), e); + } + catch (UnsupportedEncodingException e) + { + throw new SaslException(e.getMessage(), e); + } + } + + @Override + public boolean isComplete() + { + return _state == State.COMPLETE; + } + + @Override + public String getAuthorizationID() + { + return _username; + } + + @Override + public byte[] unwrap(final byte[] incoming, final int offset, final int len) throws SaslException + { + throw new IllegalStateException("No security layer supported"); + } + + @Override + public byte[] wrap(final byte[] outgoing, final int offset, final int len) throws SaslException + { + throw new IllegalStateException("No security layer supported"); + } + + @Override + public Object getNegotiatedProperty(final String propName) + { + return null; + } + + @Override + public void dispose() throws SaslException + { + + } + + private byte[] computeHmacSHA1(final byte[] key, final String string) + throws SaslException, UnsupportedEncodingException + { + Mac mac = createSha1Hmac(key); + mac.update(string.getBytes(ASCII)); + return mac.doFinal(); + } + + + private Mac createSha1Hmac(final byte[] keyBytes) + throws SaslException + { + try + { + SecretKeySpec key = new SecretKeySpec(keyBytes, "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(key); + return mac; + } + catch (NoSuchAlgorithmException e) + { + throw new SaslException(e.getMessage(), e); + } + catch (InvalidKeyException e) + { + throw new SaslException(e.getMessage(), e); + } + } + +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java index dbe8bf22a0..da84580a94 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractDurableConfiguredObjectRecoverer.java @@ -27,10 +27,9 @@ public abstract class AbstractDurableConfiguredObjectRecoverer<T> implements Dur { @Override public void load(final DurableConfigurationRecoverer durableConfigurationRecoverer, - final UUID id, - final Map<String, Object> attributes) + final ConfiguredObjectRecord record) { - final UnresolvedObject obj = createUnresolvedObject(id, getType(), attributes); + final UnresolvedObject obj = createUnresolvedObject(record); UnresolvedDependency[] dependencies = obj.getUnresolvedDependencies(); for(final UnresolvedDependency dependency : dependencies) { @@ -53,7 +52,7 @@ public abstract class AbstractDurableConfiguredObjectRecoverer<T> implements Dur dependency.resolve(o); if(obj.getUnresolvedDependencies().length == 0) { - durableConfigurationRecoverer.resolve(getType(), id, obj.resolve()); + durableConfigurationRecoverer.resolve(getType(), record.getId(), obj.resolve()); } } }); @@ -61,17 +60,15 @@ public abstract class AbstractDurableConfiguredObjectRecoverer<T> implements Dur } if(obj.getUnresolvedDependencies().length == 0) { - durableConfigurationRecoverer.resolve(getType(), id, obj.resolve()); + durableConfigurationRecoverer.resolve(getType(), record.getId(), obj.resolve()); } else { - durableConfigurationRecoverer.addUnresolvedObject(getType(), id, obj); + durableConfigurationRecoverer.addUnresolvedObject(getType(), record.getId(), obj); } } - public abstract UnresolvedObject<T> createUnresolvedObject(final UUID id, - final String type, - final Map<String, Object> attributes); + public abstract UnresolvedObject<T> createUnresolvedObject(final ConfiguredObjectRecord record); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java index a6424a3d28..ad3e685004 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java @@ -24,6 +24,8 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.ref.SoftReference; import java.nio.ByteBuffer; import java.nio.charset.Charset; @@ -37,6 +39,8 @@ import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -49,6 +53,7 @@ import org.apache.qpid.server.message.EnqueueableMessage; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.plugin.MessageMetaDataType; import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.transport.ConnectionOpen; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonParseException; @@ -71,20 +76,22 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC private static final String META_DATA_TABLE_NAME = "QPID_MESSAGE_METADATA"; private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT"; - private static final String LINKS_TABLE_NAME = "QPID_LINKS"; - private static final String BRIDGES_TABLE_NAME = "QPID_BRIDGES"; private static final String XID_TABLE_NAME = "QPID_XIDS"; private static final String XID_ACTIONS_TABLE_NAME = "QPID_XID_ACTIONS"; private static final String CONFIGURED_OBJECTS_TABLE_NAME = "QPID_CONFIGURED_OBJECTS"; + private static final String CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME = "QPID_CONFIGURED_OBJECT_HIERARCHY"; + private static final int DEFAULT_CONFIG_VERSION = 0; public static final Set<String> CONFIGURATION_STORE_TABLE_NAMES = new HashSet<String>(Arrays.asList(CONFIGURED_OBJECTS_TABLE_NAME, CONFIGURATION_VERSION_TABLE_NAME)); - public static final Set<String> MESSAGE_STORE_TABLE_NAMES = new HashSet<String>(Arrays.asList(DB_VERSION_TABLE_NAME, META_DATA_TABLE_NAME, MESSAGE_CONTENT_TABLE_NAME, QUEUE_ENTRY_TABLE_NAME, BRIDGES_TABLE_NAME, LINKS_TABLE_NAME, XID_TABLE_NAME, XID_ACTIONS_TABLE_NAME)); - + public static final Set<String> MESSAGE_STORE_TABLE_NAMES = new HashSet<String>(Arrays.asList(DB_VERSION_TABLE_NAME, + META_DATA_TABLE_NAME, MESSAGE_CONTENT_TABLE_NAME, + QUEUE_ENTRY_TABLE_NAME, + XID_TABLE_NAME, XID_ACTIONS_TABLE_NAME)); - private static final int DB_VERSION = 7; + private static final int DB_VERSION = 8; private final AtomicLong _messageId = new AtomicLong(0); @@ -116,34 +123,6 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC private static final String DELETE_FROM_META_DATA = "DELETE FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; private static final String SELECT_ALL_FROM_META_DATA = "SELECT message_id, meta_data FROM " + META_DATA_TABLE_NAME; - private static final String SELECT_FROM_LINKS = - "SELECT create_time, arguments FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and id_msb"; - private static final String DELETE_FROM_LINKS = "DELETE FROM " + LINKS_TABLE_NAME - + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_LINKS = "SELECT id_lsb, id_msb, create_time, " - + "arguments FROM " + LINKS_TABLE_NAME; - private static final String FIND_LINK = "SELECT id_lsb, id_msb FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and" - + " id_msb = ?"; - private static final String INSERT_INTO_LINKS = "INSERT INTO " + LINKS_TABLE_NAME + "( id_lsb, " - + "id_msb, create_time, arguments ) values (?, ?, ?, ?)"; - private static final String SELECT_FROM_BRIDGES = - "SELECT create_time, link_id_lsb, link_id_msb, arguments FROM " - + BRIDGES_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; - private static final String DELETE_FROM_BRIDGES = "DELETE FROM " + BRIDGES_TABLE_NAME - + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_BRIDGES = "SELECT id_lsb, id_msb, " - + " create_time," - + " link_id_lsb, link_id_msb, " - + "arguments FROM " + BRIDGES_TABLE_NAME - + " WHERE link_id_lsb = ? and link_id_msb = ?"; - private static final String FIND_BRIDGE = "SELECT id_lsb, id_msb FROM " + BRIDGES_TABLE_NAME + - " WHERE id_lsb = ? and id_msb = ?"; - private static final String INSERT_INTO_BRIDGES = "INSERT INTO " + BRIDGES_TABLE_NAME + "( id_lsb, id_msb, " - + "create_time, " - + "link_id_lsb, link_id_msb, " - + "arguments )" - + " values (?, ?, ?, ?, ?, ?)"; - private static final String INSERT_INTO_XIDS = "INSERT INTO "+ XID_TABLE_NAME +" ( format, global_id, branch_id ) values (?, ?, ?)"; private static final String DELETE_FROM_XIDS = "DELETE FROM " + XID_TABLE_NAME @@ -167,6 +146,14 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC + " where id = ?"; private static final String SELECT_FROM_CONFIGURED_OBJECTS = "SELECT id, object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME; + + private static final String INSERT_INTO_CONFIGURED_OBJECT_HIERARCHY = "INSERT INTO " + CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME + + " ( child_id, parent_type, parent_id) VALUES (?,?,?)"; + + private static final String DELETE_FROM_CONFIGURED_OBJECT_HIERARCHY = "DELETE FROM " + CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME + + " where child_id = ?"; + private static final String SELECT_FROM_CONFIGURED_OBJECT_HIERARCHY = "SELECT child_id, parent_type, parent_id FROM " + CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME; + protected static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); @@ -198,6 +185,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC private StateManager _configurationStoreStateManager; private boolean _initialized; + public AbstractJDBCMessageStore() { _messageStoreStateManager = new StateManager(_eventManager); @@ -233,13 +221,13 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } @Override - public void recoverConfigurationStore(ConfigurationRecoveryHandler recoveryHandler) + public void recoverConfigurationStore(ConfiguredObject<?> parent, ConfigurationRecoveryHandler recoveryHandler) { _configurationStoreStateManager.attainState(State.ACTIVATING); - try { createOrOpenConfigurationStoreDatabase(); + upgradeIfVersionTableExists(parent); recoveryHandler.beginConfigurationRecovery(this, getConfigVersion()); loadConfiguredObjects(recoveryHandler); setConfigVersion(recoveryHandler.completeConfigurationRecovery()); @@ -251,6 +239,25 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC _configurationStoreStateManager.attainState(State.ACTIVE); } + private void upgradeIfVersionTableExists(ConfiguredObject<?> parent) + throws SQLException { + Connection conn = newAutoCommitConnection(); + try + { + if (tableExists(DB_VERSION_TABLE_NAME, conn)) + { + upgradeIfNecessary(parent); + } + } + finally + { + if (conn != null) + { + conn.close(); + } + } + } + @Override public void openMessageStore(String virtualHostName, Map<String, Object> messageStoreSettings) { @@ -260,13 +267,13 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } @Override - public void recoverMessageStore(MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) + public void recoverMessageStore(ConfiguredObject<?> parent, MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) { _messageStoreStateManager.attainState(State.ACTIVATING); try { createOrOpenMessageStoreDatabase(); - upgradeIfNecessary(); + upgradeIfNecessary(parent); } catch (SQLException e) { @@ -302,7 +309,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC _messageStoreStateManager.attainState(State.ACTIVE); } - protected void upgradeIfNecessary() throws SQLException + protected void upgradeIfNecessary(ConfiguredObject<?> parent) throws SQLException { Connection conn = newAutoCommitConnection(); try @@ -323,6 +330,8 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC { case 6: upgradeFromV6(); + case 7: + upgradeFromV7(parent); case DB_VERSION: return; default: @@ -351,6 +360,135 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC updateDbVersion(7); } + + private void upgradeFromV7(ConfiguredObject<?> parent) throws SQLException + { + Connection connection = newConnection(); + try + { + Map<UUID,Map<String,Object>> bindingsToUpdate = new HashMap<UUID, Map<String, Object>>(); + List<UUID> others = new ArrayList<UUID>(); + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(_module); + + PreparedStatement stmt = connection.prepareStatement(SELECT_FROM_CONFIGURED_OBJECTS); + try + { + ResultSet rs = stmt.executeQuery(); + try + { + while (rs.next()) + { + UUID id = UUID.fromString(rs.getString(1)); + String objectType = rs.getString(2); + Map<String,Object> attributes = objectMapper.readValue(getBlobAsString(rs, 3),Map.class); + if(objectType.endsWith("Binding")) + { + bindingsToUpdate.put(id,attributes); + } + else + { + others.add(id); + } + } + } + catch (JsonMappingException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + catch (JsonParseException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + catch (IOException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + stmt = connection.prepareStatement(INSERT_INTO_CONFIGURED_OBJECT_HIERARCHY); + try + { + for (UUID id : others) + { + stmt.setString(1, id.toString()); + stmt.setString(2, "VirtualHost"); + stmt.setString(3, parent.getId().toString()); + stmt.execute(); + } + for(Map.Entry<UUID, Map<String,Object>> bindingEntry : bindingsToUpdate.entrySet()) + { + stmt.setString(1, bindingEntry.getKey().toString()); + stmt.setString(2,"Queue"); + stmt.setString(3, bindingEntry.getValue().remove("queue").toString()); + stmt.execute(); + + stmt.setString(1, bindingEntry.getKey().toString()); + stmt.setString(2,"Exchange"); + stmt.setString(3, bindingEntry.getValue().remove("exchange").toString()); + stmt.execute(); + } + } + finally + { + stmt.close(); + } + stmt = connection.prepareStatement(UPDATE_CONFIGURED_OBJECTS); + try + { + for(Map.Entry<UUID, Map<String,Object>> bindingEntry : bindingsToUpdate.entrySet()) + { + stmt.setString(1, "Binding"); + byte[] attributesAsBytes = objectMapper.writeValueAsBytes(bindingEntry.getValue()); + + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + stmt.setBinaryStream(2, bis, attributesAsBytes.length); + stmt.setString(3, bindingEntry.getKey().toString()); + stmt.execute(); + } + } + catch (JsonMappingException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + catch (JsonGenerationException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + catch (IOException e) + { + throw new StoreException("Error recovering persistent state: " + e.getMessage(), e); + } + finally + { + stmt.close(); + } + stmt = connection.prepareStatement(UPDATE_DB_VERSION); + try + { + stmt.setInt(1, 8); + stmt.execute(); + } + finally + { + stmt.close(); + } + connection.commit(); + } + finally + { + connection.close(); + } + } + private void updateDbVersion(int newVersion) throws SQLException { Connection conn = newAutoCommitConnection(); @@ -392,8 +530,6 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC createQueueEntryTable(conn); createMetaDataTable(conn); createMessageContentTable(conn); - createLinkTable(conn); - createBridgeTable(conn); createXidTable(conn); createXidActionTable(conn); conn.close(); @@ -405,6 +541,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC createConfigVersionTable(conn); createConfiguredObjectsTable(conn); + createConfiguredObjectHierarchyTable(conn); conn.close(); } @@ -480,6 +617,23 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } } + private void createConfiguredObjectHierarchyTable(final Connection conn) throws SQLException + { + if(!tableExists(CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE " + CONFIGURED_OBJECT_HIERARCHY_TABLE_NAME + + " ( child_id VARCHAR(36) not null, parent_type varchar(255), parent_id VARCHAR(36), PRIMARY KEY (child_id, parent_type))"); + } + finally + { + stmt.close(); + } + } + } + private void createQueueEntryTable(final Connection conn) throws SQLException @@ -546,45 +700,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } - private void createLinkTable(final Connection conn) throws SQLException - { - if(!tableExists(LINKS_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute("CREATE TABLE "+ LINKS_TABLE_NAME +" ( id_lsb " + getSqlBigIntType() + " not null," - + " id_msb " + getSqlBigIntType() + " not null," - + " create_time " + getSqlBigIntType() + " not null," - + " arguments "+getSqlBlobType()+", PRIMARY KEY ( id_lsb, id_msb ))"); - } - finally - { - stmt.close(); - } - } - } - private void createBridgeTable(final Connection conn) throws SQLException - { - if(!tableExists(BRIDGES_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute("CREATE TABLE "+ BRIDGES_TABLE_NAME +" ( id_lsb " + getSqlBigIntType() + " not null," - + " id_msb " + getSqlBigIntType() + " not null," - + " create_time " + getSqlBigIntType() + " not null," - + " link_id_lsb " + getSqlBigIntType() + " not null," - + " link_id_msb " + getSqlBigIntType() + " not null," - + " arguments "+getSqlBlobType()+", PRIMARY KEY ( id_lsb, id_msb ))"); - } - finally - { - stmt.close(); - } - } - } private void createXidTable(final Connection conn) throws SQLException { @@ -826,38 +942,28 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC @Override - public void create(UUID id, String type, Map<String,Object> attributes) throws StoreException - { - if (_configurationStoreStateManager.isInState(State.ACTIVE)) - { - insertConfiguredObject(new ConfiguredObjectRecord(id, type, attributes)); - } - - } - - @Override - public void remove(UUID id, String type) throws StoreException - { - int results = removeConfiguredObject(id); - if (results == 0) - { - throw new StoreException(type + " with id " + id + " not found"); - } - } - - @Override - public void update(UUID id, String type, Map<String, Object> attributes) throws StoreException + public void create(ConfiguredObjectRecord object) throws StoreException { if (_configurationStoreStateManager.isInState(State.ACTIVE)) { - ConfiguredObjectRecord queueConfiguredObject = loadConfiguredObject(id); - if (queueConfiguredObject != null) + try { - ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(id, type, attributes); - updateConfiguredObject(newQueueRecord); + Connection conn = newConnection(); + try + { + insertConfiguredObject(object, conn); + conn.commit(); + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new StoreException("Error creating ConfiguredObject " + object); } } - } /** @@ -1997,66 +2103,65 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC _eventManager.addEventListener(eventListener, events); } - private void insertConfiguredObject(ConfiguredObjectRecord configuredObject) throws StoreException + private void insertConfiguredObject(ConfiguredObjectRecord configuredObject, final Connection conn) throws StoreException { if (_configurationStoreStateManager.isInState(State.ACTIVE)) { try { - Connection conn = newAutoCommitConnection(); + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); try { - PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + stmt.setString(1, configuredObject.getId().toString()); + ResultSet rs = stmt.executeQuery(); + boolean exists; try { - stmt.setString(1, configuredObject.getId().toString()); - ResultSet rs = stmt.executeQuery(); + exists = rs.next(); + + } + finally + { + rs.close(); + } + // If we don't have any data in the result set then we can add this configured object + if (!exists) + { + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECTS); try { - // If we don't have any data in the result set then we can add this configured object - if (!rs.next()) + insertStmt.setString(1, configuredObject.getId().toString()); + insertStmt.setString(2, configuredObject.getType()); + if(configuredObject.getAttributes() == null) { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECTS); - try - { - insertStmt.setString(1, configuredObject.getId().toString()); - insertStmt.setString(2, configuredObject.getType()); - if(configuredObject.getAttributes() == null) - { - insertStmt.setNull(3, Types.BLOB); - } - else - { - final Map<String, Object> attributes = configuredObject.getAttributes(); - final ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.registerModule(_module); - byte[] attributesAsBytes = objectMapper.writeValueAsBytes(attributes); - - ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); - insertStmt.setBinaryStream(3, bis, attributesAsBytes.length); - } - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } + insertStmt.setNull(3, Types.BLOB); } + else + { + final Map<String, Object> attributes = configuredObject.getAttributes(); + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(_module); + byte[] attributesAsBytes = objectMapper.writeValueAsBytes(attributes); + + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + insertStmt.setBinaryStream(3, bis, attributesAsBytes.length); + } + insertStmt.execute(); } finally { - rs.close(); + insertStmt.close(); } + + writeHierarchy(configuredObject, conn); } - finally - { - stmt.close(); - } + } finally { - conn.close(); + stmt.close(); } + } catch (JsonMappingException e) { @@ -2075,31 +2180,11 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC throw new StoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); } } - } - private int removeConfiguredObject(UUID id) throws StoreException - { - int results = 0; - try - { - Connection conn = newAutoCommitConnection(); - try - { - results = removeConfiguredObject(id, conn); - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new StoreException("Error deleting of configured object with id " + id + " from database: " + e.getMessage(), e); - } - return results; } - public UUID[] removeConfiguredObjects(UUID... objects) throws StoreException + @Override + public UUID[] remove(ConfiguredObjectRecord... objects) throws StoreException { Collection<UUID> removed = new ArrayList<UUID>(objects.length); try @@ -2108,11 +2193,11 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC Connection conn = newAutoCommitConnection(); try { - for(UUID id : objects) + for(ConfiguredObjectRecord record : objects) { - if(removeConfiguredObject(id, conn) != 0) + if(removeConfiguredObject(record.getId(), conn) != 0) { - removed.add(id); + removed.add(record.getId()); } } } @@ -2130,7 +2215,8 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC private int removeConfiguredObject(final UUID id, final Connection conn) throws SQLException { - final int results;PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECTS); + final int results; + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECTS); try { stmt.setString(1, id.toString()); @@ -2140,30 +2226,18 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC { stmt.close(); } - return results; - } - - private void updateConfiguredObject(final ConfiguredObjectRecord configuredObject) throws StoreException - { - if (_configurationStoreStateManager.isInState(State.ACTIVE)) + stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECT_HIERARCHY); + try { - try - { - Connection conn = newAutoCommitConnection(); - try - { - updateConfiguredObject(configuredObject, false, conn); - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new StoreException("Error updating configured object " + configuredObject + " in database: " + e.getMessage(), e); - } + stmt.setString(1, id.toString()); + stmt.executeUpdate(); + } + finally + { + stmt.close(); } + + return results; } public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException @@ -2258,6 +2332,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC { insertStmt.close(); } + writeHierarchy(configuredObject, conn); } } finally @@ -2284,72 +2359,31 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } - private ConfiguredObjectRecord loadConfiguredObject(final UUID id) throws StoreException + private void writeHierarchy(final ConfiguredObjectRecord configuredObject, final Connection conn) throws SQLException, StoreException { - ConfiguredObjectRecord result = null; + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECT_HIERARCHY); try { - Connection conn = newAutoCommitConnection(); - try + for(Map.Entry<String,ConfiguredObjectRecord> parentEntry : configuredObject.getParents().entrySet()) { - PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); - try - { - stmt.setString(1, id.toString()); - ResultSet rs = stmt.executeQuery(); - try - { - if (rs.next()) - { - String type = rs.getString(1); - String attributes = getBlobAsString(rs, 2); - result = new ConfiguredObjectRecord(id, type, - (new ObjectMapper()).readValue(attributes,Map.class)); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); + insertStmt.setString(1, configuredObject.getId().toString()); + insertStmt.setString(2, parentEntry.getKey()); + insertStmt.setString(3, parentEntry.getValue().getId().toString()); + + insertStmt.execute(); } } - catch (JsonMappingException e) - { - throw new StoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); - } - catch (JsonParseException e) - { - throw new StoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); - } - catch (IOException e) - { - throw new StoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); - } - catch (SQLException e) + finally { - throw new StoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); + insertStmt.close(); } - return result; } private void loadConfiguredObjects(ConfigurationRecoveryHandler recoveryHandler) throws SQLException, StoreException { Connection conn = newAutoCommitConnection(); - + Map<UUID, ConfiguredObjectRecordImpl> configuredObjects = new HashMap<UUID, ConfiguredObjectRecordImpl>(); final ObjectMapper objectMapper = new ObjectMapper(); try { @@ -2364,8 +2398,11 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC String id = rs.getString(1); String objectType = rs.getString(2); String attributes = getBlobAsString(rs, 3); - recoveryHandler.configuredObject(UUID.fromString(id), objectType, - objectMapper.readValue(attributes,Map.class)); + final ConfiguredObjectRecordImpl configuredObjectRecord = + new ConfiguredObjectRecordImpl(UUID.fromString(id), objectType, + objectMapper.readValue(attributes, Map.class)); + configuredObjects.put(configuredObjectRecord.getId(),configuredObjectRecord); + } } catch (JsonMappingException e) @@ -2389,11 +2426,53 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC { stmt.close(); } + stmt = conn.prepareStatement(SELECT_FROM_CONFIGURED_OBJECT_HIERARCHY); + try + { + ResultSet rs = stmt.executeQuery(); + try + { + while (rs.next()) + { + UUID childId = UUID.fromString(rs.getString(1)); + String parentType = rs.getString(2); + UUID parentId = UUID.fromString(rs.getString(3)); + + ConfiguredObjectRecordImpl child = configuredObjects.get(childId); + ConfiguredObjectRecordImpl parent = configuredObjects.get(parentId); + + if(child != null && parent != null) + { + child.addParent(parentType, parent); + } + else if(child != null && child.getType().endsWith("Binding") && parentType.equals("Exchange")) + { + // TODO - remove this hack for amq. exchanges + child.addParent(parentType, new ConfiguredObjectRecordImpl(parentId, parentType, Collections.<String,Object>emptyMap())); + } + + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } finally { conn.close(); } + + for(ConfiguredObjectRecord record : configuredObjects.values()) + { + recoveryHandler.configuredObject(record); + } } protected abstract String getBlobAsString(ResultSet rs, int col) throws SQLException; @@ -2441,4 +2520,51 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } } + + private static final class ConfiguredObjectRecordImpl implements ConfiguredObjectRecord + { + + private final UUID _id; + private final String _type; + private final Map<String, Object> _attributes; + private final Map<String, ConfiguredObjectRecord> _parents = new HashMap<String, ConfiguredObjectRecord>(); + + private ConfiguredObjectRecordImpl(final UUID id, + final String type, + final Map<String, Object> attributes) + { + _id = id; + _type = type; + _attributes = Collections.unmodifiableMap(attributes); + } + + @Override + public UUID getId() + { + return _id; + } + + @Override + public String getType() + { + return _type; + } + + private void addParent(String parentType, ConfiguredObjectRecord parent) + { + _parents.put(parentType, parent); + } + + @Override + public Map<String, Object> getAttributes() + { + return _attributes; + } + + @Override + public Map<String, ConfiguredObjectRecord> getParents() + { + return Collections.unmodifiableMap(_parents); + } + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java index afdeb257e0..bf538e4592 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractMemoryMessageStore.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicLong; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.model.ConfiguredObject; /** A simple message store that stores the messages in a thread-safe structure in memory. */ abstract public class AbstractMemoryMessageStore extends NullMessageStore @@ -83,7 +84,7 @@ abstract public class AbstractMemoryMessageStore extends NullMessageStore } @Override - public void recoverConfigurationStore(ConfigurationRecoveryHandler recoveryHandler) + public void recoverConfigurationStore(ConfiguredObject<?> parent, ConfigurationRecoveryHandler recoveryHandler) { } @@ -96,7 +97,7 @@ abstract public class AbstractMemoryMessageStore extends NullMessageStore } @Override - public void recoverMessageStore(MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) + public void recoverMessageStore(ConfiguredObject<?> parent, MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) { _stateManager.attainState(State.ACTIVATING); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java index c6ebe90802..c8aef92a95 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java @@ -27,7 +27,7 @@ public interface ConfigurationRecoveryHandler { void beginConfigurationRecovery(DurableConfigurationStore store, int configVersion); - void configuredObject(UUID id, String type, Map<String, Object> attributes); + void configuredObject(ConfiguredObjectRecord object); /** * diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java index 44490385d9..99f8f0f04a 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java @@ -20,69 +20,16 @@ */ package org.apache.qpid.server.store; -import java.util.Collections; -import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; -public class ConfiguredObjectRecord +public interface ConfiguredObjectRecord { - private UUID _id; - private String _type; - private Map<String,Object> _attributes; + UUID getId(); - public ConfiguredObjectRecord(UUID id, String type, Map<String,Object> attributes) - { - super(); - _id = id; - _type = type; - _attributes = Collections.unmodifiableMap(new LinkedHashMap<String,Object>(attributes)); - } + String getType(); - public UUID getId() - { - return _id; - } + Map<String,Object> getAttributes(); - public String getType() - { - return _type; - } - - public Map<String,Object> getAttributes() - { - return _attributes; - } - - @Override - public String toString() - { - return "ConfiguredObjectRecord [id=" + _id + ", type=" + _type + ", attributes=" + _attributes + "]"; - } - - @Override - public boolean equals(Object o) - { - if(this == o) - { - return true; - } - if(o == null || getClass() != o.getClass()) - { - return false; - } - - ConfiguredObjectRecord that = (ConfiguredObjectRecord) o; - - return _type.equals(that._type) && _id.equals(that._id) && _attributes.equals(that._attributes); - } - - @Override - public int hashCode() - { - int result = _id.hashCode(); - result = 31 * result + _type.hashCode(); - result = 31 * result + _attributes.hashCode(); - return result; - } + Map<String, ConfiguredObjectRecord> getParents(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecordImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecordImpl.java new file mode 100644 index 0000000000..ed553aa823 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecordImpl.java @@ -0,0 +1,104 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.store; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.UUID; + +public class ConfiguredObjectRecordImpl implements ConfiguredObjectRecord +{ + private UUID _id; + private String _type; + private final Map<String,Object> _attributes; + private final Map<String,ConfiguredObjectRecord> _parents; + + public ConfiguredObjectRecordImpl(UUID id, String type, Map<String, Object> attributes) + { + this(id,type,attributes,Collections.<String,ConfiguredObjectRecord>emptyMap()); + } + + public ConfiguredObjectRecordImpl(UUID id, String type, Map<String, Object> attributes, Map<String,ConfiguredObjectRecord> parents) + { + super(); + _id = id; + _type = type; + _attributes = Collections.unmodifiableMap(new LinkedHashMap<String,Object>(attributes)); + _parents = Collections.unmodifiableMap(new LinkedHashMap<String, ConfiguredObjectRecord>(parents)); + } + + @Override + public UUID getId() + { + return _id; + } + + @Override + public String getType() + { + return _type; + } + + @Override + public Map<String,Object> getAttributes() + { + return _attributes; + } + + @Override + public Map<String, ConfiguredObjectRecord> getParents() + { + return _parents; + } + + @Override + public String toString() + { + return "ConfiguredObjectRecord [id=" + _id + ", type=" + _type + ", attributes=" + _attributes + "]"; + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + + ConfiguredObjectRecordImpl that = (ConfiguredObjectRecordImpl) o; + + return _type.equals(that._type) && _id.equals(that._id) && _attributes.equals(that._attributes); + } + + @Override + public int hashCode() + { + int result = _id.hashCode(); + result = 31 * result + _type.hashCode(); + result = 31 * result + _attributes.hashCode(); + return result; + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java index 6696426c0e..f8d8ecdd7c 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationRecoverer.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.store; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.UUID; @@ -76,19 +77,19 @@ public class DurableConfigurationRecoverer implements ConfigurationRecoveryHandl } @Override - public void configuredObject(final UUID id, final String type, final Map<String, Object> attributes) + public void configuredObject(ConfiguredObjectRecord record) { - _upgrader.configuredObject(id, type, attributes); + _upgrader.configuredObject(record); } - void onConfiguredObject(final UUID id, final String type, final Map<String, Object> attributes) + void onConfiguredObject(ConfiguredObjectRecord record) { - DurableConfiguredObjectRecoverer recoverer = getRecoverer(type); + DurableConfiguredObjectRecoverer recoverer = getRecoverer(record.getType()); if(recoverer == null) { - throw new IllegalConfigurationException("Unknown type for configured object: " + type); + throw new IllegalConfigurationException("Unknown type for configured object: " + record.getType()); } - recoverer.load(this, id, attributes); + recoverer.load(this, record); } private DurableConfiguredObjectRecoverer getRecoverer(final String type) @@ -111,19 +112,12 @@ public class DurableConfigurationRecoverer implements ConfigurationRecoveryHandl private void applyUpgrade() { - final Collection<ConfiguredObjectRecord> updates = new ArrayList<ConfiguredObjectRecord>(); - final Collection<UUID> deletes = new ArrayList<UUID>(); - for(Map.Entry<UUID,ConfiguredObjectRecord> entry : _upgrader.getUpdatedRecords().entrySet()) - { - if(entry.getValue() != null) - { - updates.add(entry.getValue()); - } - else - { - deletes.add(entry.getKey()); - } - } + final Collection<ConfiguredObjectRecord> updates = new HashSet<ConfiguredObjectRecord>(_upgrader.getUpdatedRecords().values()); + final Collection<ConfiguredObjectRecord> deletes = new HashSet<ConfiguredObjectRecord>(_upgrader.getDeletedRecords().values()); + + // Due to the way the upgraders work it is possible that the updates list may contain nulls + updates.remove(null); + deletes.remove(null); if(!updates.isEmpty()) { @@ -131,7 +125,7 @@ public class DurableConfigurationRecoverer implements ConfigurationRecoveryHandl } if(!deletes.isEmpty()) { - _store.removeConfiguredObjects(deletes.toArray(new UUID[deletes.size()])); + _store.remove(deletes.toArray(new ConfiguredObjectRecord[deletes.size()])); } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java index 10e56f1f71..918a9b0134 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java @@ -23,6 +23,8 @@ package org.apache.qpid.server.store; import java.util.Map; import java.util.UUID; +import org.apache.qpid.server.model.ConfiguredObject; + public interface DurableConfigurationStore { String STORE_TYPE = "storeType"; @@ -43,48 +45,40 @@ public interface DurableConfigurationStore /** * Recovers configuration from the store using given recovery handler - * + * @param parent parent * @param recoveryHandler recovery handler */ - void recoverConfigurationStore(ConfigurationRecoveryHandler recoveryHandler) throws StoreException; + void recoverConfigurationStore(ConfiguredObject<?> parent, ConfigurationRecoveryHandler recoveryHandler) throws StoreException; /** * Makes the specified object persistent. * - * @param id The id of the object to persist. - * @param type The type of the object to persist - * @param attributes the attributes of the object to persist + * @param object The object to persist. * * @throws StoreException If the operation fails for any reason. */ - void create(UUID id, String type, Map<String, Object> attributes) throws StoreException; + void create(ConfiguredObjectRecord object) throws StoreException; /** - * Removes the specified persistent configured object. + * Removes the specified persistent configured objects. * - * @param id The id of the object to remove. - * @param type The type of the object to remove + * @param objects The objects to remove. * * @throws StoreException If the operation fails for any reason. */ - void remove(UUID id, String type) throws StoreException; - - public UUID[] removeConfiguredObjects(UUID... objects) throws StoreException; + public UUID[] remove(ConfiguredObjectRecord... objects) throws StoreException; /** - * Updates the specified object in the persistent store, IF it is already present. If the object - * is not present in the store, it will not be added. + * Updates the specified objects in the persistent store, IF it is already present. If the object + * is not present in the store, it will only be added if createIfNecessary is set to true, otherwise an exception + * will be thrown. * - * @param id The id of the object to update. - * @param type The type of the object to update - * @param attributes the updated attributes + * @param createIfNecessary if false then will fail if the object does not exist. + * @param records the records to update * * @throws StoreException If the operation fails for any reason. */ - void update(UUID id, String type, Map<String, Object> attributes) throws StoreException; - - void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException; diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java index f914389b0e..9410006d65 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java @@ -20,12 +20,8 @@ */ package org.apache.qpid.server.store; -import java.security.PrivilegedAction; import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -35,12 +31,8 @@ import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.model.Binding; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Exchange; -import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.security.*; - -import javax.security.auth.Subject; public class DurableConfigurationStoreHelper { @@ -58,7 +50,7 @@ public class DurableConfigurationStoreHelper { attributesMap.put(Queue.ALTERNATE_EXCHANGE, queue.getAlternateExchange().getId()); } - store.update(queue.getId(), QUEUE, attributesMap); + store.update(false, new ConfiguredObjectRecordImpl(queue.getId(), QUEUE, attributesMap)); } public static void createQueue(DurableConfigurationStore store, AMQQueue<?> queue) @@ -71,49 +63,33 @@ public class DurableConfigurationStoreHelper attributesMap.put(Queue.ALTERNATE_EXCHANGE, queue.getAlternateExchange().getId()); } - store.create(queue.getId(), QUEUE, attributesMap); + store.create(new ConfiguredObjectRecordImpl(queue.getId(), QUEUE, attributesMap)); } public static void removeQueue(DurableConfigurationStore store, AMQQueue queue) { - store.remove(queue.getId(), QUEUE); + store.remove(queue.asObjectRecord()); } public static void createExchange(DurableConfigurationStore store, ExchangeImpl exchange) { - Map<String, Object> attributesMap = exchange.getActualAttributes(); - attributesMap.remove(ConfiguredObject.ID); - - store.create(exchange.getId(), EXCHANGE, attributesMap); - + store.create(exchange.asObjectRecord()); } - public static void removeExchange(DurableConfigurationStore store, ExchangeImpl exchange) { - store.remove(exchange.getId(), EXCHANGE); + store.remove(exchange.asObjectRecord()); } public static void createBinding(DurableConfigurationStore store, final BindingImpl binding) { - Map<String, Object> attributesMap = binding.getActualAttributes(); - attributesMap.remove(ConfiguredObject.ID); - if(!attributesMap.containsKey(Binding.EXCHANGE)) - { - attributesMap.put(Binding.EXCHANGE, binding.getExchange()); - } - if(!attributesMap.containsKey(Binding.QUEUE)) - { - attributesMap.put(Binding.QUEUE, binding.getQueue()); - } - - store.create(binding.getId(), BINDING, attributesMap); + store.create(binding.asObjectRecord()); } public static void removeBinding(DurableConfigurationStore store, BindingImpl binding) { - store.remove(binding.getId(), BINDING); + store.remove(binding.asObjectRecord()); } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreUpgrader.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreUpgrader.java index 1d3e4cc672..45b2ff56d7 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreUpgrader.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreUpgrader.java @@ -25,11 +25,12 @@ import java.util.UUID; public interface DurableConfigurationStoreUpgrader { - void configuredObject(UUID id, String type, Map<String, Object> attributes); + void configuredObject(ConfiguredObjectRecord record); void complete(); void setNextUpgrader(DurableConfigurationStoreUpgrader upgrader); Map<UUID, ConfiguredObjectRecord> getUpdatedRecords(); + Map<UUID, ConfiguredObjectRecord> getDeletedRecords(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java index e065728bd3..4c618f5089 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfiguredObjectRecoverer.java @@ -26,8 +26,7 @@ import java.util.UUID; public interface DurableConfiguredObjectRecoverer { public void load(final DurableConfigurationRecoverer durableConfigurationRecoverer, - final UUID id, - final Map<String, Object> attributes); + final ConfiguredObjectRecord record); public String getType(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java index a1512bbc22..97559c8286 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/JsonFileConfigStore.java @@ -26,22 +26,22 @@ import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Model; import org.apache.qpid.server.model.VirtualHost; +import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.JsonProcessingException; +import org.codehaus.jackson.Version; import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.JsonSerializer; +import org.codehaus.jackson.map.Module; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; +import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.module.SimpleModule; public class JsonFileConfigStore implements DurableConfigurationStore { @@ -61,8 +61,30 @@ public class JsonFileConfigStore implements DurableConfigurationStore private String _backupFileName; private int _configVersion; + private static final Module _module; + static + { + SimpleModule module= new SimpleModule("ConfiguredObjectSerializer", new Version(1,0,0,null)); + + final JsonSerializer<ConfiguredObject> serializer = new JsonSerializer<ConfiguredObject>() + { + @Override + public void serialize(final ConfiguredObject value, + final JsonGenerator jgen, + final SerializerProvider provider) + throws IOException, JsonProcessingException + { + jgen.writeString(value.getId().toString()); + } + }; + module.addSerializer(ConfiguredObject.class, serializer); + + _module = module; + } + public JsonFileConfigStore() { + _objectMapper.registerModule(_module); _objectMapper.enable(SerializationConfig.Feature.INDENT_OUTPUT); } @@ -76,13 +98,13 @@ public class JsonFileConfigStore implements DurableConfigurationStore } @Override - public void recoverConfigurationStore(ConfigurationRecoveryHandler recoveryHandler) + public void recoverConfigurationStore(ConfiguredObject<?> parent, ConfigurationRecoveryHandler recoveryHandler) { recoveryHandler.beginConfigurationRecovery(this,_configVersion); List<ConfiguredObjectRecord> records = new ArrayList<ConfiguredObjectRecord>(_objectsById.values()); for(ConfiguredObjectRecord record : records) { - recoveryHandler.configuredObject(record.getId(), record.getType(), record.getAttributes()); + recoveryHandler.configuredObject(record); } int oldConfigVersion = _configVersion; _configVersion = recoveryHandler.completeConfigurationRecovery(); @@ -287,20 +309,21 @@ public class JsonFileConfigStore implements DurableConfigurationStore } } + Map<String,UUID> parentMap = new HashMap<String, UUID>(); if(parentId != null) { - data.put(parentClass.getSimpleName().toLowerCase(),parentId); + parentMap.put(parentClass.getSimpleName(),parentId); for(Class<? extends ConfiguredObject> otherParent : MODEL.getParentTypes(clazz)) { if(otherParent != parentClass) { final String otherParentAttr = otherParent.getSimpleName().toLowerCase(); - Object otherParentId = data.get(otherParentAttr); + Object otherParentId = data.remove(otherParentAttr); if(otherParentId instanceof String) { try { - data.put(otherParentAttr, UUID.fromString((String) otherParentId)); + parentMap.put(otherParent.getSimpleName(), UUID.fromString((String) otherParentId)); } catch(IllegalArgumentException e) { @@ -312,7 +335,7 @@ public class JsonFileConfigStore implements DurableConfigurationStore } } - _objectsById.put(id, new ConfiguredObjectRecord(id, type, data)); + _objectsById.put(id, new ConfiguredObjectRecordImpl(id, type, data, parentMap)); List<UUID> idsForType = _idsByType.get(type); if(idsForType == null) { @@ -324,28 +347,27 @@ public class JsonFileConfigStore implements DurableConfigurationStore } @Override - public synchronized void create(final UUID id, final String type, final Map<String, Object> attributes) throws - StoreException + public synchronized void create(ConfiguredObjectRecord record) throws StoreException { - if(_objectsById.containsKey(id)) + if(_objectsById.containsKey(record.getId())) { - throw new StoreException("Object with id " + id + " already exists"); + throw new StoreException("Object with id " + record.getId() + " already exists"); } - else if(!CLASS_NAME_MAPPING.containsKey(type)) + else if(!CLASS_NAME_MAPPING.containsKey(record.getType())) { - throw new StoreException("Cannot create object of unknown type " + type); + throw new StoreException("Cannot create object of unknown type " + record.getType()); } else { - ConfiguredObjectRecord record = new ConfiguredObjectRecord(id, type, attributes); - _objectsById.put(id, record); - List<UUID> idsForType = _idsByType.get(type); + + _objectsById.put(record.getId(), record); + List<UUID> idsForType = _idsByType.get(record.getType()); if(idsForType == null) { idsForType = new ArrayList<UUID>(); - _idsByType.put(type, idsForType); + _idsByType.put(record.getType(), idsForType); } - idsForType.add(id); + idsForType.add(record.getId()); save(); } } @@ -400,7 +422,21 @@ public class JsonFileConfigStore implements DurableConfigurationStore Map<String,Object> map = new LinkedHashMap<String, Object>(); map.put("id", id); map.putAll(record.getAttributes()); - map.remove(MODEL.getParentTypes(type).iterator().next().getSimpleName().toLowerCase()); + + Collection<Class<? extends ConfiguredObject>> parentTypes = MODEL.getParentTypes(type); + if(parentTypes.size() > 1) + { + Iterator<Class<? extends ConfiguredObject>> iter = parentTypes.iterator(); + // skip the first parent, which is given by structure + iter.next(); + // for all other parents add a fake attribute with name being the parent type in lower case, and the value + // being the parents id + while(iter.hasNext()) + { + String parentType = iter.next().getSimpleName(); + map.put(parentType.toLowerCase(), record.getParents().get(parentType).getId()); + } + } Collection<Class<? extends ConfiguredObject>> childClasses = new ArrayList<Class<? extends ConfiguredObject>>(MODEL.getChildTypes(type)); @@ -418,8 +454,10 @@ public class JsonFileConfigStore implements DurableConfigurationStore for(UUID childId : childIds) { ConfiguredObjectRecord childRecord = _objectsById.get(childId); - final String parentArg = type.getSimpleName().toLowerCase(); - if(id.toString().equals(String.valueOf(childRecord.getAttributes().get(parentArg)))) + + final ConfiguredObjectRecord parent = childRecord.getParents().get(type.getSimpleName()); + String parentId = parent.getId().toString(); + if(id.toString().equals(parentId)) { entities.add(build(childClass,childId)); } @@ -436,34 +474,22 @@ public class JsonFileConfigStore implements DurableConfigurationStore } @Override - public void remove(final UUID id, final String type) throws StoreException - { - removeConfiguredObjects(id); - } - - @Override - public synchronized UUID[] removeConfiguredObjects(final UUID... objects) throws StoreException + public synchronized UUID[] remove(final ConfiguredObjectRecord... objects) throws StoreException { List<UUID> removedIds = new ArrayList<UUID>(); - for(UUID id : objects) + for(ConfiguredObjectRecord requestedRecord : objects) { - ConfiguredObjectRecord record = _objectsById.remove(id); + ConfiguredObjectRecord record = _objectsById.remove(requestedRecord.getId()); if(record != null) { - removedIds.add(id); - _idsByType.get(record.getType()).remove(id); + removedIds.add(record.getId()); + _idsByType.get(record.getType()).remove(record.getId()); } } save(); return removedIds.toArray(new UUID[removedIds.size()]); } - @Override - public void update(final UUID id, final String type, final Map<String, Object> attributes) throws - StoreException - { - update(false, new ConfiguredObjectRecord(id, type, attributes)); - } @Override public void update(final boolean createIfNecessary, final ConfiguredObjectRecord... records) @@ -554,5 +580,59 @@ public class JsonFileConfigStore implements DurableConfigurationStore return map; } + private class ConfiguredObjectRecordImpl implements ConfiguredObjectRecord + { + + private final UUID _id; + private final String _type; + private final Map<String, Object> _attributes; + private final Map<String, UUID> _parents; + + private ConfiguredObjectRecordImpl(final UUID id, final String type, final Map<String, Object> attributes, + final Map<String, UUID> parents) + { + _id = id; + _type = type; + _attributes = attributes; + _parents = parents; + } + + @Override + public UUID getId() + { + return _id; + } + + @Override + public String getType() + { + return _type; + } + + @Override + public Map<String, Object> getAttributes() + { + return _attributes; + } + + @Override + public Map<String, ConfiguredObjectRecord> getParents() + { + Map<String,ConfiguredObjectRecord> parents = new HashMap<String, ConfiguredObjectRecord>(); + for(Map.Entry<String,UUID> entry : _parents.entrySet()) + { + ConfiguredObjectRecord value = _objectsById.get(entry.getValue()); + + if(value == null && entry.getKey().equals("Exchange")) + { + // TODO - remove this hack for the defined exchanges + value = new ConfiguredObjectRecordImpl(entry.getValue(),entry.getKey(),Collections.<String,Object>emptyMap(), Collections.<String,UUID>emptyMap()); + } + + parents.put(entry.getKey(), value); + } + return parents; + } + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java index fb99191477..7fb6c4df48 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/MessageStore.java @@ -22,6 +22,8 @@ package org.apache.qpid.server.store; import java.util.Map; +import org.apache.qpid.server.model.ConfiguredObject; + /** * MessageStore defines the interface to a storage area, which can be used to preserve the state of messages. * @@ -43,11 +45,11 @@ public interface MessageStore /** * Called after opening to recover messages and transactions with given recovery handlers - * + * @param parent TODO * @param messageRecoveryHandler * @param transactionLogRecoveryHandler */ - void recoverMessageStore(MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler); + void recoverMessageStore(ConfiguredObject<?> parent, MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler); public <T extends StorableMessageMetaData> StoredMessage<T> addMessage(T metaData); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NonNullUpgrader.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NonNullUpgrader.java index a671e93b26..1abd3de34a 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NonNullUpgrader.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NonNullUpgrader.java @@ -28,6 +28,7 @@ public abstract class NonNullUpgrader implements DurableConfigurationStoreUpgrad { private DurableConfigurationStoreUpgrader _nextUpgrader; private final Map<UUID, ConfiguredObjectRecord> _updates = new HashMap<UUID, ConfiguredObjectRecord>(); + private final Map<UUID, ConfiguredObjectRecord> _deletes = new HashMap<UUID, ConfiguredObjectRecord>(); public final void setNextUpgrader(final DurableConfigurationStoreUpgrader upgrader) { @@ -50,6 +51,10 @@ public abstract class NonNullUpgrader implements DurableConfigurationStoreUpgrad { return _updates; } + protected Map<UUID, ConfiguredObjectRecord> getDeleteMap() + { + return _deletes; + } @Override public final Map<UUID, ConfiguredObjectRecord> getUpdatedRecords() @@ -59,4 +64,11 @@ public abstract class NonNullUpgrader implements DurableConfigurationStoreUpgrad return updates; } + @Override + public final Map<UUID, ConfiguredObjectRecord> getDeletedRecords() + { + final Map<UUID, ConfiguredObjectRecord> deletes = new HashMap<UUID, ConfiguredObjectRecord>(_deletes); + deletes.putAll(_nextUpgrader.getDeletedRecords()); + return deletes; + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java index c095675602..66c3fe6cae 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullMessageStore.java @@ -22,6 +22,8 @@ package org.apache.qpid.server.store; import java.util.Map; import java.util.UUID; +import org.apache.qpid.server.model.ConfiguredObject; + public abstract class NullMessageStore implements MessageStore, DurableConfigurationStore { @Override @@ -30,12 +32,7 @@ public abstract class NullMessageStore implements MessageStore, DurableConfigura } @Override - public void recoverConfigurationStore(ConfigurationRecoveryHandler recoveryHandler) - { - } - - @Override - public void update(UUID id, String type, Map<String, Object> attributes) + public void recoverConfigurationStore(ConfiguredObject<?> parent, ConfigurationRecoveryHandler recoveryHandler) { } @@ -44,20 +41,19 @@ public abstract class NullMessageStore implements MessageStore, DurableConfigura { } - - @Override - public void remove(UUID id, String type) - { - } - @Override - public UUID[] removeConfiguredObjects(final UUID... objects) + public UUID[] remove(final ConfiguredObjectRecord... objects) { - return objects; + final UUID[] removed = new UUID[objects.length]; + for(int i = 0; i < objects.length; i++) + { + removed[i] = objects[i].getId(); + } + return removed; } @Override - public void create(UUID id, String type, Map<String, Object> attributes) + public void create(ConfiguredObjectRecord record) { } @@ -95,7 +91,7 @@ public abstract class NullMessageStore implements MessageStore, DurableConfigura } @Override - public void recoverMessageStore(MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) + public void recoverMessageStore(ConfiguredObject<?> parent, MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) { } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullUpgrader.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullUpgrader.java index c8a812fa89..b17dd4a6f3 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullUpgrader.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/NullUpgrader.java @@ -34,9 +34,9 @@ public final class NullUpgrader implements DurableConfigurationStoreUpgrader } @Override - public void configuredObject(final UUID id, final String type, final Map<String, Object> attributes) + public void configuredObject(final ConfiguredObjectRecord record) { - _durableConfigurationRecoverer.onConfiguredObject(id, type, attributes); + _durableConfigurationRecoverer.onConfiguredObject(record); } @Override @@ -55,4 +55,10 @@ public final class NullUpgrader implements DurableConfigurationStoreUpgrader { return Collections.emptyMap(); } + + @Override + public Map<UUID, ConfiguredObjectRecord> getDeletedRecords() + { + return Collections.emptyMap(); + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java index a14b1ad8c1..e41ee051f3 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java @@ -137,7 +137,7 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg _eventLogger.message(VirtualHostMessages.CREATED(_name)); - _securityManager = new SecurityManager(parentSecurityManager, virtualHost.getSecurityAcl(), _name); + _securityManager = parentSecurityManager; _connectionRegistry = new ConnectionRegistry(); _connectionRegistry.addRegistryChangeListener(this); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/BindingRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/BindingRecoverer.java index e6577e04e4..58721ea8b2 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/BindingRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/BindingRecoverer.java @@ -28,9 +28,11 @@ import org.apache.log4j.Logger; import org.apache.qpid.server.binding.BindingImpl; import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.store.AbstractDurableConfiguredObjectRecoverer; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.store.UnresolvedDependency; import org.apache.qpid.server.store.UnresolvedObject; @@ -49,11 +51,9 @@ public class BindingRecoverer extends AbstractDurableConfiguredObjectRecoverer<B } @Override - public UnresolvedObject<BindingImpl> createUnresolvedObject(final UUID id, - final String type, - final Map<String, Object> attributes) + public UnresolvedObject<BindingImpl> createUnresolvedObject(ConfiguredObjectRecord record) { - return new UnresolvedBinding(id, attributes); + return new UnresolvedBinding(record); } @Override @@ -76,12 +76,11 @@ public class BindingRecoverer extends AbstractDurableConfiguredObjectRecoverer<B private ExchangeImpl _exchange; private AMQQueue _queue; - public UnresolvedBinding(final UUID id, - final Map<String, Object> attributeMap) + public UnresolvedBinding(final ConfiguredObjectRecord record) { - _bindingId = id; - _exchangeId = UUID.fromString(String.valueOf(attributeMap.get(org.apache.qpid.server.model.Binding.EXCHANGE))); - _queueId = UUID.fromString(String.valueOf(attributeMap.get(org.apache.qpid.server.model.Binding.QUEUE))); + _bindingId = record.getId(); + _exchangeId = record.getParents().get(Exchange.class.getSimpleName()).getId(); + _queueId = record.getParents().get(Queue.class.getSimpleName()).getId(); _exchange = _exchangeRegistry.getExchange(_exchangeId); if(_exchange == null) { @@ -93,8 +92,8 @@ public class BindingRecoverer extends AbstractDurableConfiguredObjectRecoverer<B _unresolvedDependencies.add(new QueueDependency()); } - _bindingName = (String) attributeMap.get(org.apache.qpid.server.model.Binding.NAME); - _bindingArgumentsMap = (Map<String, Object>) attributeMap.get(org.apache.qpid.server.model.Binding.ARGUMENTS); + _bindingName = (String) record.getAttributes().get(org.apache.qpid.server.model.Binding.NAME); + _bindingArgumentsMap = (Map<String, Object>) record.getAttributes().get(org.apache.qpid.server.model.Binding.ARGUMENTS); } @Override diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java index 2d824cbd2d..f6fa6344d3 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/DefaultUpgraderProvider.java @@ -39,6 +39,7 @@ import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.QueueArgumentsConverter; import org.apache.qpid.server.store.ConfiguredObjectRecord; +import org.apache.qpid.server.store.ConfiguredObjectRecordImpl; import org.apache.qpid.server.store.DurableConfigurationRecoverer; import org.apache.qpid.server.store.DurableConfigurationStoreUpgrader; import org.apache.qpid.server.store.NonNullUpgrader; @@ -133,9 +134,9 @@ public class DefaultUpgraderProvider implements UpgraderProvider } @Override - public void configuredObject(final UUID id, final String type, Map<String, Object> attributes) + public void configuredObject(final ConfiguredObjectRecord record) { - _records.put(id, new ConfiguredObjectRecord(id, type, attributes)); + _records.put(record.getId(), record); } private void removeSelectorArguments(Map<String, Object> binding) @@ -147,9 +148,9 @@ public class DefaultUpgraderProvider implements UpgraderProvider binding.put(Binding.ARGUMENTS, arguments); } - private boolean isTopicExchange(Map<String, Object> binding) + private boolean isTopicExchange(ConfiguredObjectRecord entry) { - UUID exchangeId = UUID.fromString((String)binding.get(Binding.EXCHANGE)); + UUID exchangeId = entry.getParents().get("Exchange").getId(); if(_records.containsKey(exchangeId)) { @@ -188,17 +189,17 @@ public class DefaultUpgraderProvider implements UpgraderProvider String type = record.getType(); Map<String, Object> attributes = record.getAttributes(); UUID id = record.getId(); - if(type.equals(Binding.class.getName()) && hasSelectorArguments(attributes) && !isTopicExchange(attributes)) + if(type.equals(Binding.class.getName()) && hasSelectorArguments(attributes) && !isTopicExchange(record)) { attributes = new LinkedHashMap<String, Object>(attributes); removeSelectorArguments(attributes); - record = new ConfiguredObjectRecord(id, type, attributes); + record = new ConfiguredObjectRecordImpl(id, type, attributes, record.getParents()); getUpdateMap().put(id, record); entry.setValue(record); } - getNextUpgrader().configuredObject(id, type, attributes); + getNextUpgrader().configuredObject(record); } getNextUpgrader().complete(); @@ -212,12 +213,14 @@ public class DefaultUpgraderProvider implements UpgraderProvider */ private class Version1Upgrader extends NonNullUpgrader { + + @Override - public void configuredObject(final UUID id, String type, final Map<String, Object> attributes) + public void configuredObject(final ConfiguredObjectRecord record) { - type = type.substring(1+type.lastIndexOf('.')); - getUpdateMap().put(id, new ConfiguredObjectRecord(id, type, attributes)); - + String type = record.getType().substring(1 + record.getType().lastIndexOf('.')); + getUpdateMap().put(record.getId(), + new ConfiguredObjectRecordImpl(record.getId(), type, record.getAttributes(), record.getParents())); } @Override @@ -226,22 +229,24 @@ public class DefaultUpgraderProvider implements UpgraderProvider for(Map.Entry<UUID, ConfiguredObjectRecord> entry : getUpdateMap().entrySet()) { final ConfiguredObjectRecord record = entry.getValue(); - if(isBinding(record.getType()) && (unknownExchange((String) record.getAttributes().get(Binding.EXCHANGE)) - || unknownQueue((String) record.getAttributes().get(Binding.QUEUE)))) + final ConfiguredObjectRecord exchangeParent = record.getParents().get(Exchange.class.getSimpleName()); + final ConfiguredObjectRecord queueParent = record.getParents().get(Queue.class.getSimpleName()); + if(isBinding(record.getType()) && (exchangeParent == null || unknownExchange(exchangeParent.getId()) + || queueParent == null || unknownQueue(queueParent.getId()))) { + getDeleteMap().put(entry.getKey(), entry.getValue()); entry.setValue(null); } else { - getNextUpgrader().configuredObject(record.getId(), record.getType(), record.getAttributes()); + getNextUpgrader().configuredObject(record); } } getNextUpgrader().complete(); } - private boolean unknownExchange(final String exchangeIdString) + private boolean unknownExchange(final UUID exchangeId) { - UUID exchangeId = UUID.fromString(exchangeIdString); if (_defaultExchangeIds.containsValue(exchangeId)) { return false; @@ -251,9 +256,8 @@ public class DefaultUpgraderProvider implements UpgraderProvider || _exchangeRegistry.getExchange(exchangeId) != null); } - private boolean unknownQueue(final String queueIdString) + private boolean unknownQueue(final UUID queueId) { - UUID queueId = UUID.fromString(queueIdString); ConfiguredObjectRecord localRecord = getUpdateMap().get(queueId); return !((localRecord != null && localRecord.getType().equals(Queue.class.getSimpleName())) || _virtualHost.getQueue(queueId) != null); @@ -277,22 +281,24 @@ public class DefaultUpgraderProvider implements UpgraderProvider private static final String ARGUMENTS = "arguments"; @Override - public void configuredObject(UUID id, String type, Map<String, Object> attributes) + public void configuredObject(ConfiguredObjectRecord record) { - if(Queue.class.getSimpleName().equals(type)) + + if(Queue.class.getSimpleName().equals(record.getType())) { Map<String, Object> newAttributes = new LinkedHashMap<String, Object>(); - if(attributes.get(ARGUMENTS) instanceof Map) + if(record.getAttributes().get(ARGUMENTS) instanceof Map) { - newAttributes.putAll(QueueArgumentsConverter.convertWireArgsToModel((Map<String, Object>) attributes + newAttributes.putAll(QueueArgumentsConverter.convertWireArgsToModel((Map<String, Object>) record.getAttributes() .get(ARGUMENTS))); } - newAttributes.putAll(attributes); - attributes = newAttributes; - getUpdateMap().put(id, new ConfiguredObjectRecord(id,type,attributes)); + newAttributes.putAll(record.getAttributes()); + + record = new ConfiguredObjectRecordImpl(record.getId(), record.getType(), newAttributes, record.getParents()); + getUpdateMap().put(record.getId(), record); } - getNextUpgrader().configuredObject(id,type,attributes); + getNextUpgrader().configuredObject(record); } @Override @@ -311,16 +317,17 @@ public class DefaultUpgraderProvider implements UpgraderProvider { @Override - public void configuredObject(UUID id, String type, Map<String, Object> attributes) + public void configuredObject(ConfiguredObjectRecord record) { - if(Queue.class.getSimpleName().equals(type)) + + if(Queue.class.getSimpleName().equals(record.getType())) { - Map<String, Object> newAttributes = new LinkedHashMap<String, Object>(attributes); - if(attributes.get(EXCLUSIVE) instanceof Boolean) + Map<String, Object> newAttributes = new LinkedHashMap<String, Object>(record.getAttributes()); + if(record.getAttributes().get(EXCLUSIVE) instanceof Boolean) { - boolean isExclusive = (Boolean) attributes.get(EXCLUSIVE); + boolean isExclusive = (Boolean) record.getAttributes().get(EXCLUSIVE); newAttributes.put(EXCLUSIVE, isExclusive ? "CONTAINER" : "NONE"); - if(!isExclusive && attributes.containsKey("owner")) + if(!isExclusive && record.getAttributes().containsKey("owner")) { newAttributes.remove("owner"); } @@ -329,15 +336,16 @@ public class DefaultUpgraderProvider implements UpgraderProvider { newAttributes.remove("owner"); } - if(!attributes.containsKey("durable")) + if(!record.getAttributes().containsKey("durable")) { newAttributes.put("durable","true"); } - attributes = newAttributes; - getUpdateMap().put(id, new ConfiguredObjectRecord(id,type,attributes)); + + record = new ConfiguredObjectRecordImpl(record.getId(),record.getType(),newAttributes, record.getParents()); + getUpdateMap().put(record.getId(), record); } - getNextUpgrader().configuredObject(id,type,attributes); + getNextUpgrader().configuredObject(record); } @Override @@ -352,15 +360,16 @@ public class DefaultUpgraderProvider implements UpgraderProvider private Map<String, String> _missingAmqpExchanges = new HashMap<String, String>(DEFAULT_EXCHANGES); @Override - public void configuredObject(UUID id, String type, Map<String, Object> attributes) + public void configuredObject(ConfiguredObjectRecord record) { - if(Exchange.class.getSimpleName().equals(type)) + if(Exchange.class.getSimpleName().equals(record.getType())) { + Map<String, Object> attributes = record.getAttributes(); String name = (String)attributes.get(NAME); _missingAmqpExchanges.remove(name); } - getNextUpgrader().configuredObject(id,type,attributes); + getNextUpgrader().configuredObject(record); } @Override @@ -383,9 +392,11 @@ public class DefaultUpgraderProvider implements UpgraderProvider attributes.put(org.apache.qpid.server.model.Exchange.DURABLE, true); - getUpdateMap().put(id, new ConfiguredObjectRecord(id, Exchange.class.getSimpleName(), attributes)); + ConfiguredObjectRecord virtualHostRecord = new ConfiguredObjectRecordImpl(_virtualHost.getId(), org.apache.qpid.server.model.VirtualHost.class.getSimpleName(), Collections.<String, Object>emptyMap()); + ConfiguredObjectRecord record = new ConfiguredObjectRecordImpl(id, Exchange.class.getSimpleName(), attributes, Collections.singletonMap(virtualHostRecord.getType(), virtualHostRecord)); + getUpdateMap().put(id, record); - getNextUpgrader().configuredObject(id, Exchange.class.getSimpleName(), attributes); + getNextUpgrader().configuredObject(record); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/ExchangeRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/ExchangeRecoverer.java index 2743b0ef59..4431fc786d 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/ExchangeRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/ExchangeRecoverer.java @@ -28,6 +28,7 @@ import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.store.AbstractDurableConfiguredObjectRecoverer; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.store.UnresolvedDependency; import org.apache.qpid.server.store.UnresolvedObject; import org.apache.qpid.server.util.ServerScopedRuntimeException; @@ -50,11 +51,9 @@ public class ExchangeRecoverer extends AbstractDurableConfiguredObjectRecoverer< } @Override - public UnresolvedObject<ExchangeImpl> createUnresolvedObject(final UUID id, - final String type, - final Map<String, Object> attributes) + public UnresolvedObject<ExchangeImpl> createUnresolvedObject(final ConfiguredObjectRecord record) { - return new UnresolvedExchange(id, attributes); + return new UnresolvedExchange(record.getId(), record.getAttributes()); } private class UnresolvedExchange implements UnresolvedObject<ExchangeImpl> diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueRecoverer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueRecoverer.java index 385f4e219e..eb1bc883ca 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueRecoverer.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueRecoverer.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.QueueFactory; import org.apache.qpid.server.store.AbstractDurableConfiguredObjectRecoverer; +import org.apache.qpid.server.store.ConfiguredObjectRecord; import org.apache.qpid.server.store.UnresolvedDependency; import org.apache.qpid.server.store.UnresolvedObject; @@ -58,11 +59,9 @@ public class QueueRecoverer extends AbstractDurableConfiguredObjectRecoverer<AMQ } @Override - public UnresolvedObject<AMQQueue> createUnresolvedObject(final UUID id, - final String type, - final Map<String, Object> attributes) + public UnresolvedObject<AMQQueue> createUnresolvedObject(final ConfiguredObjectRecord record) { - return new UnresolvedQueue(id, type, attributes); + return new UnresolvedQueue(record.getId(), record.getAttributes()); } private class UnresolvedQueue implements UnresolvedObject<AMQQueue> @@ -75,7 +74,6 @@ public class QueueRecoverer extends AbstractDurableConfiguredObjectRecoverer<AMQ private ExchangeImpl _alternateExchange; public UnresolvedQueue(final UUID id, - final String type, final Map<String, Object> attributes) { _attributes = attributes; diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java index 58a0e689cb..bb3f8fc012 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/StandardVirtualHost.java @@ -96,13 +96,13 @@ public class StandardVirtualHost extends AbstractVirtualHost _messageStore.openMessageStore(virtualHost.getName(), virtualHost.getMessageStoreSettings()); - _durableConfigurationStore.recoverConfigurationStore(configRecoverer); + _durableConfigurationStore.recoverConfigurationStore(getModel(), configRecoverer); // If store does not have entries for standard exchanges (amq.*), the following will create them. initialiseModel(); VirtualHostConfigRecoveryHandler recoveryHandler = new VirtualHostConfigRecoveryHandler(this); - _messageStore.recoverMessageStore(recoveryHandler, recoveryHandler); + _messageStore.recoverMessageStore(getModel(), recoveryHandler, recoveryHandler); attainActivation(); } diff --git a/qpid/java/broker-core/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AuthenticationManagerFactory b/qpid/java/broker-core/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AuthenticationManagerFactory index 8ff67030ef..a1139b386c 100644 --- a/qpid/java/broker-core/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AuthenticationManagerFactory +++ b/qpid/java/broker-core/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.AuthenticationManagerFactory @@ -22,3 +22,5 @@ org.apache.qpid.server.security.auth.manager.ExternalAuthenticationManagerFactor org.apache.qpid.server.security.auth.manager.KerberosAuthenticationManagerFactory org.apache.qpid.server.security.auth.manager.PlainPasswordFileAuthenticationManagerFactory org.apache.qpid.server.security.auth.manager.SimpleLDAPAuthenticationManagerFactory +org.apache.qpid.server.security.auth.manager.ScramSHA1AuthenticationManagerFactory + diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java index c23c4715e8..c5786fb981 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListenerTest.java @@ -63,7 +63,9 @@ public class StoreConfigurationChangeListenerTest extends QpidTestCase { notifyBrokerStarted(); Broker broker = mock(Broker.class); + when(broker.getCategoryClass()).thenReturn(Broker.class); VirtualHost child = mock(VirtualHost.class); + when(child.getCategoryClass()).thenReturn(VirtualHost.class); _listener.childAdded(broker, child); verify(_store).save(any(ConfigurationEntry.class), any(ConfigurationEntry.class)); } @@ -72,7 +74,9 @@ public class StoreConfigurationChangeListenerTest extends QpidTestCase { notifyBrokerStarted(); Broker broker = mock(Broker.class); + when(broker.getCategoryClass()).thenReturn(Broker.class); VirtualHost child = mock(VirtualHost.class); + when(child.getCategoryClass()).thenReturn(VirtualHost.class); _listener.childRemoved(broker, child); verify(_store).save(any(ConfigurationEntry.class)); } @@ -81,6 +85,7 @@ public class StoreConfigurationChangeListenerTest extends QpidTestCase { notifyBrokerStarted(); Broker broker = mock(Broker.class); + when(broker.getCategoryClass()).thenReturn(Broker.class); _listener.attributeSet(broker, Broker.QUEUE_FLOW_CONTROL_SIZE_BYTES, null, 1); verify(_store).save(any(ConfigurationEntry.class)); } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/SecurityManagerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/SecurityManagerTest.java new file mode 100644 index 0000000000..e27981d22c --- /dev/null +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/SecurityManagerTest.java @@ -0,0 +1,571 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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 static org.apache.qpid.server.security.access.ObjectType.BROKER; +import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE; +import static org.apache.qpid.server.security.access.Operation.ACCESS_LOGS; +import static org.apache.qpid.server.security.access.Operation.PUBLISH; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.security.AccessControlException; + +import org.apache.qpid.server.binding.BindingImpl; +import org.apache.qpid.server.consumer.ConsumerImpl; +import org.apache.qpid.server.exchange.ExchangeImpl; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectProperties.Property; +import org.apache.qpid.server.security.access.ObjectType; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.access.OperationLoggingDetails; +import org.apache.qpid.test.utils.QpidTestCase; + +public class SecurityManagerTest extends QpidTestCase +{ + private static final String TEST_EXCHANGE_TYPE = "testExchangeType"; + private static final String TEST_VIRTUAL_HOST = "testVirtualHost"; + private static final String TEST_EXCHANGE = "testExchange"; + private static final String TEST_QUEUE = "testQueue"; + + private AccessControl _accessControl; + private SecurityManager _securityManager; + private VirtualHost<?> _virtualHost; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _accessControl = mock(AccessControl.class); + _virtualHost = mock(VirtualHost.class); + + AccessControlProvider<?> aclProvider = mock(AccessControlProvider.class); + when(aclProvider.getAccessControl()).thenReturn(_accessControl); + + when(_virtualHost.getName()).thenReturn(TEST_VIRTUAL_HOST); + + _securityManager = new SecurityManager(mock(Broker.class), false); + _securityManager.stateChanged(aclProvider, State.INITIALISING, State.ACTIVE); + } + + public void testAuthoriseCreateBinding() + { + ExchangeImpl<?> exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + + AMQQueue<?> queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + when(queue.isDurable()).thenReturn(true); + when(queue.getLifetimePolicy()).thenReturn(LifetimePolicy.PERMANENT); + + BindingImpl binding = mock(BindingImpl.class); + when(binding.getExchange()).thenReturn(exchange); + when(binding.getAMQQueue()).thenReturn(queue); + when(binding.getBindingKey()).thenReturn("bindingKey"); + + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_EXCHANGE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.QUEUE_NAME, TEST_QUEUE); + properties.put(Property.ROUTING_KEY, "bindingKey"); + properties.put(Property.TEMPORARY, false); + properties.put(Property.DURABLE, true); + + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateBinding(binding); + verify(_accessControl).authorise(eq(Operation.BIND), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateBinding(binding); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.BIND), eq(ObjectType.EXCHANGE), eq(properties)); + } + + + public void testAuthoriseMethod() + { + ObjectProperties properties = new ObjectProperties("testMethod"); + properties.put(ObjectProperties.Property.COMPONENT, "testComponent"); + properties.put(ObjectProperties.Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseMethod(Operation.UPDATE, "testComponent", "testMethod", TEST_VIRTUAL_HOST); + verify(_accessControl).authorise(eq(Operation.UPDATE), eq(ObjectType.METHOD), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseMethod(Operation.UPDATE, "testComponent", "testMethod", TEST_VIRTUAL_HOST); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.UPDATE), eq(ObjectType.METHOD), eq(properties)); + } + + public void testAccessManagement() + { + configureAccessPlugin(Result.ALLOWED); + _securityManager.accessManagement(); + verify(_accessControl).authorise(Operation.ACCESS, ObjectType.MANAGEMENT, ObjectProperties.EMPTY); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.accessManagement(); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(Operation.ACCESS, ObjectType.MANAGEMENT, ObjectProperties.EMPTY); + } + + public void testAuthoriseCreateConnection() + { + AMQConnectionModel<?,?> connection = mock(AMQConnectionModel.class); + when(connection.getVirtualHostName()).thenReturn(TEST_VIRTUAL_HOST); + + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_VIRTUAL_HOST); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateConnection(connection); + verify(_accessControl).authorise(eq(Operation.ACCESS), eq(ObjectType.VIRTUALHOST), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateConnection(connection); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.ACCESS), eq(ObjectType.VIRTUALHOST), eq(properties)); + } + + public void testAuthoriseCreateConsumer() + { + AMQQueue<?> queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + when(queue.isDurable()).thenReturn(true); + when(queue.getLifetimePolicy()).thenReturn(LifetimePolicy.PERMANENT); + + ConsumerImpl consumer = mock(ConsumerImpl.class); + when(consumer.getMessageSource()).thenReturn(queue); + + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_QUEUE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.AUTO_DELETE, false); + properties.put(Property.TEMPORARY, false); + properties.put(Property.DURABLE, true); + properties.put(Property.EXCLUSIVE, false); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateConsumer(consumer); + verify(_accessControl).authorise(eq(Operation.CONSUME), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateConsumer(consumer); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CONSUME), eq(ObjectType.QUEUE), eq(properties)); + } + + public void testAuthoriseCreateExchange() + { + ExchangeImpl<?> exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + when(exchange.getTypeName()).thenReturn(TEST_EXCHANGE_TYPE); + + ObjectProperties properties = createExpectedExchangeObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateExchange(exchange); + verify(_accessControl).authorise(eq(Operation.CREATE), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateExchange(exchange); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CREATE), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthoriseCreateQueue() + { + AMQQueue<?> queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + + ObjectProperties properties = createExpectedQueueObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseCreateQueue(queue); + verify(_accessControl).authorise(eq(Operation.CREATE), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseCreateQueue(queue); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CREATE), eq(ObjectType.QUEUE), eq(properties)); + } + + public void testAuthoriseDeleteQueue() + { + AMQQueue<?> queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + + ObjectProperties properties = createExpectedQueueObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseDelete(queue); + verify(_accessControl).authorise(eq(Operation.DELETE), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseDelete(queue); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.DELETE), eq(ObjectType.QUEUE), eq(properties)); + } + + public void testAuthoriseUpdateQueue() + { + AMQQueue<?> queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + + ObjectProperties properties = createExpectedQueueObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseUpdate(queue); + verify(_accessControl).authorise(eq(Operation.UPDATE), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseUpdate(queue); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.UPDATE), eq(ObjectType.QUEUE), eq(properties)); + } + + public void testAuthoriseUpdateExchange() + { + ExchangeImpl<?> exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + when(exchange.getTypeName()).thenReturn(TEST_EXCHANGE_TYPE); + + ObjectProperties properties = createExpectedExchangeObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseUpdate(exchange); + verify(_accessControl).authorise(eq(Operation.UPDATE), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseUpdate(exchange); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.UPDATE), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthoriseDeleteExchange() + { + ExchangeImpl<?> exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + when(exchange.getTypeName()).thenReturn(TEST_EXCHANGE_TYPE); + + ObjectProperties properties = createExpectedExchangeObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseDelete(exchange); + verify(_accessControl).authorise(eq(Operation.DELETE), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseDelete(exchange); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.DELETE), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthoriseGroupOperation() + { + ObjectProperties properties = new ObjectProperties("testGroup"); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseGroupOperation(Operation.CREATE, "testGroup"); + verify(_accessControl).authorise(eq(Operation.CREATE), eq(ObjectType.GROUP), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseGroupOperation(Operation.CREATE, "testGroup"); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CREATE), eq(ObjectType.GROUP), eq(properties)); + } + + public void testAuthoriseUserOperation() + { + ObjectProperties properties = new ObjectProperties("testUser"); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseUserOperation(Operation.CREATE, "testUser"); + verify(_accessControl).authorise(eq(Operation.CREATE), eq(ObjectType.USER), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseUserOperation(Operation.CREATE, "testUser"); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.CREATE), eq(ObjectType.USER), eq(properties)); + } + + public void testAuthorisePublish() + { + String routingKey = "routingKey"; + String exchangeName = "exchangeName"; + boolean immediate = true; + ObjectProperties properties = new ObjectProperties(TEST_VIRTUAL_HOST, exchangeName, routingKey, immediate); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authorisePublish(immediate, routingKey, exchangeName, TEST_VIRTUAL_HOST); + verify(_accessControl).authorise(eq(Operation.PUBLISH), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authorisePublish(immediate, routingKey, exchangeName, TEST_VIRTUAL_HOST); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.PUBLISH), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthorisePurge() + { + AMQQueue<?> queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + + ObjectProperties properties = createExpectedQueueObjectProperties(); + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authorisePurge(queue); + verify(_accessControl).authorise(eq(Operation.PURGE), eq(ObjectType.QUEUE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authorisePurge(queue); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.PURGE), eq(ObjectType.QUEUE), eq(properties)); + } + + + public void testAuthoriseUnbind() + { + ExchangeImpl<?> exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getName()).thenReturn(TEST_EXCHANGE); + + AMQQueue<?> queue = mock(AMQQueue.class); + when(queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(queue.getName()).thenReturn(TEST_QUEUE); + when(queue.isDurable()).thenReturn(true); + when(queue.getLifetimePolicy()).thenReturn(LifetimePolicy.PERMANENT); + + BindingImpl binding = mock(BindingImpl.class); + when(binding.getExchange()).thenReturn(exchange); + when(binding.getAMQQueue()).thenReturn(queue); + when(binding.getBindingKey()).thenReturn("bindingKey"); + + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_EXCHANGE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.QUEUE_NAME, TEST_QUEUE); + properties.put(Property.ROUTING_KEY, "bindingKey"); + properties.put(Property.TEMPORARY, false); + properties.put(Property.DURABLE, true); + + + configureAccessPlugin(Result.ALLOWED); + _securityManager.authoriseUnbind(binding); + verify(_accessControl).authorise(eq(Operation.UNBIND), eq(ObjectType.EXCHANGE), eq(properties)); + + configureAccessPlugin(Result.DENIED); + try + { + _securityManager.authoriseUnbind(binding); + fail("AccessControlException is expected"); + } + catch(AccessControlException e) + { + // pass + } + verify(_accessControl, times(2)).authorise(eq(Operation.UNBIND), eq(ObjectType.EXCHANGE), eq(properties)); + } + + public void testAuthoriseConfiguringBroker() + { + OperationLoggingDetails properties = new OperationLoggingDetails("create virtualhost 'test'"); + + configureAccessPlugin(Result.ALLOWED); + assertTrue(_securityManager.authoriseConfiguringBroker("test", VirtualHost.class, Operation.CREATE)); + verify(_accessControl).authorise(eq(Operation.CONFIGURE), eq(ObjectType.BROKER), eq(properties)); + + configureAccessPlugin(Result.DENIED); + assertFalse(_securityManager.authoriseConfiguringBroker("test", VirtualHost.class, Operation.CREATE)); + verify(_accessControl, times(2)).authorise(eq(Operation.CONFIGURE), eq(ObjectType.BROKER), eq(properties)); + } + + public void testAuthoriseLogsAccess() + { + configureAccessPlugin(Result.ALLOWED); + assertTrue(_securityManager.authoriseLogsAccess()); + verify(_accessControl).authorise(ACCESS_LOGS, BROKER, ObjectProperties.EMPTY); + + configureAccessPlugin(Result.DENIED); + assertFalse(_securityManager.authoriseLogsAccess()); + verify(_accessControl, times(2)).authorise(ACCESS_LOGS, BROKER, ObjectProperties.EMPTY); + } + + private void configureAccessPlugin(Result result) + { + when(_accessControl.authorise(any(Operation.class), any(ObjectType.class), any(ObjectProperties.class))).thenReturn(result); + } + + private ObjectProperties createExpectedExchangeObjectProperties() + { + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_EXCHANGE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.AUTO_DELETE, false); + properties.put(Property.TEMPORARY, true); + properties.put(Property.DURABLE, false); + properties.put(Property.TYPE, TEST_EXCHANGE_TYPE); + return properties; + } + + private ObjectProperties createExpectedQueueObjectProperties() + { + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.NAME, TEST_QUEUE); + properties.put(Property.VIRTUALHOST_NAME, TEST_VIRTUAL_HOST); + properties.put(Property.AUTO_DELETE, true); + properties.put(Property.TEMPORARY, true); + properties.put(Property.DURABLE, false); + properties.put(Property.EXCLUSIVE, false); + return properties; + } + + + +} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java new file mode 100644 index 0000000000..033e0afde8 --- /dev/null +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/security/auth/manager/ScramSHA1AuthenticationManagerTest.java @@ -0,0 +1,203 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 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.configuration.updater.TaskExecutor; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.User; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.test.utils.QpidTestCase; + import org.apache.qpid.server.security.SecurityManager; + +import javax.security.auth.login.AccountNotFoundException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ScramSHA1AuthenticationManagerTest extends QpidTestCase +{ + private ScramSHA1AuthenticationManager _authManager; + private Broker _broker; + private SecurityManager _securityManager; + private TaskExecutor _executor; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _executor = new TaskExecutor(); + _executor.start(); + _broker = mock(Broker.class); + _securityManager = mock(SecurityManager.class); + when(_broker.getTaskExecutor()).thenReturn(_executor); + when(_broker.getSecurityManager()).thenReturn(_securityManager); + final Map<String, Object> attributesMap = new HashMap<String, Object>(); + attributesMap.put(AuthenticationProvider.NAME, getTestName()); + attributesMap.put(AuthenticationProvider.ID, UUID.randomUUID()); + _authManager = new ScramSHA1AuthenticationManager(_broker, Collections.<String,Object>emptyMap(),attributesMap,false); + } + + @Override + public void tearDown() throws Exception + { + _executor.stop(); + super.tearDown(); + } + + public void testAddChildAndThenDelete() + { + // No children should be present before the test starts + assertEquals("No users should be present before the test starts", 0, _authManager.getChildren(User.class).size()); + assertEquals("No users should be present before the test starts", 0, _authManager.getUsers().size()); + + final Map<String, Object> childAttrs = new HashMap<String, Object>(); + + childAttrs.put(User.NAME, getTestName()); + childAttrs.put(User.PASSWORD, "password"); + User user = _authManager.addChild(User.class, childAttrs); + assertNotNull("User should be created but addChild returned null", user); + assertEquals(getTestName(), user.getName()); + // password shouldn't actually be the given string, but instead salt and the hashed value + assertFalse("Password shouldn't actually be the given string, but instead salt and the hashed value", "password".equals(user.getPassword())); + + AuthenticationResult authResult = + _authManager.authenticate(getTestName(), "password"); + + assertEquals("User should authenticate with given password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + + assertEquals("Manager should have exactly one user child",1, _authManager.getChildren(User.class).size()); + assertEquals("Manager should have exactly one user child",1, _authManager.getUsers().size()); + + + user.setDesiredState(State.ACTIVE, State.DELETED); + + assertEquals("No users should be present after child deletion", 0, _authManager.getChildren(User.class).size()); + + + authResult = _authManager.authenticate(getTestName(), "password"); + assertEquals("User should no longer authenticate with given password", AuthenticationResult.AuthenticationStatus.ERROR, authResult.getStatus()); + + } + + public void testCreateUser() + { + assertEquals("No users should be present before the test starts", 0, _authManager.getChildren(User.class).size()); + assertTrue(_authManager.createUser(getTestName(), "password", Collections.<String, String>emptyMap())); + assertEquals("Manager should have exactly one user child",1, _authManager.getChildren(User.class).size()); + User user = _authManager.getChildren(User.class).iterator().next(); + assertEquals(getTestName(), user.getName()); + // password shouldn't actually be the given string, but instead salt and the hashed value + assertFalse("Password shouldn't actually be the given string, but instead salt and the hashed value", "password".equals(user.getPassword())); + final Map<String, Object> childAttrs = new HashMap<String, Object>(); + + childAttrs.put(User.NAME, getTestName()); + childAttrs.put(User.PASSWORD, "password"); + try + { + user = _authManager.addChild(User.class, childAttrs); + fail("Should not be able to create a second user with the same name"); + } + catch(IllegalArgumentException e) + { + // pass + } + try + { + _authManager.deleteUser(getTestName()); + } + catch (AccountNotFoundException e) + { + fail("AccountNotFoundException thrown when none was expected: " + e.getMessage()); + } + try + { + _authManager.deleteUser(getTestName()); + fail("AccountNotFoundException not thrown when was expected"); + } + catch (AccountNotFoundException e) + { + // pass + } + } + + public void testUpdateUser() + { + assertTrue(_authManager.createUser(getTestName(), "password", Collections.<String, String>emptyMap())); + assertTrue(_authManager.createUser(getTestName()+"_2", "password", Collections.<String, String>emptyMap())); + assertEquals("Manager should have exactly two user children",2, _authManager.getChildren(User.class).size()); + + AuthenticationResult authResult = _authManager.authenticate(getTestName(), "password"); + + assertEquals("User should authenticate with given password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + authResult = _authManager.authenticate(getTestName()+"_2", "password"); + assertEquals("User should authenticate with given password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + + for(User user : _authManager.getChildren(User.class)) + { + if(user.getName().equals(getTestName())) + { + user.setAttributes(Collections.singletonMap(User.PASSWORD, "newpassword")); + } + } + + authResult = _authManager.authenticate(getTestName(), "newpassword"); + assertEquals("User should authenticate with updated password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + authResult = _authManager.authenticate(getTestName()+"_2", "password"); + assertEquals("User should authenticate with original password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + + authResult = _authManager.authenticate(getTestName(), "password"); + assertEquals("User not authenticate with original password", AuthenticationResult.AuthenticationStatus.ERROR, authResult.getStatus()); + + for(User user : _authManager.getChildren(User.class)) + { + if(user.getName().equals(getTestName())) + { + user.setPassword("newerpassword"); + } + } + + authResult = _authManager.authenticate(getTestName(), "newerpassword"); + assertEquals("User should authenticate with updated password", AuthenticationResult.AuthenticationStatus.SUCCESS, authResult.getStatus()); + + + + } + + public void testNonASCIIUser() + { + try + { + _authManager.createUser(getTestName()+Character.toString((char)0xa3), "password", Collections.<String, String>emptyMap()); + fail("Expected exception when attempting to create a user with a non ascii name"); + } + catch(IllegalArgumentException e) + { + // pass + } + } + +} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java index b4dfbe837d..fbd26208d8 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java @@ -21,7 +21,6 @@ package org.apache.qpid.server.store; import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; @@ -40,6 +39,9 @@ import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.server.binding.BindingImpl; import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.logging.EventLogger; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.ExclusivityPolicy; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Queue; @@ -62,6 +64,10 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest private static final String BINDING = org.apache.qpid.server.model.Binding.class.getSimpleName(); private static final String QUEUE = Queue.class.getSimpleName(); + private static final UUID ANY_UUID = UUID.randomUUID(); + private static final Map ANY_MAP = new HashMap(); + + private String _storePath; private String _storeName; @@ -91,12 +97,16 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest setTestSystemProperty("QPID_WORK", TMP_FOLDER); _recoveryHandler = mock(ConfigurationRecoveryHandler.class); - when(_exchange.getName()).thenReturn(EXCHANGE_NAME); when(_exchange.getId()).thenReturn(_exchangeId); when(_exchange.getExchangeType()).thenReturn(mock(ExchangeType.class)); when(_exchange.getEventLogger()).thenReturn(new EventLogger()); + ConfiguredObjectRecord exchangeRecord = mock(ConfiguredObjectRecord.class); + when(exchangeRecord.getId()).thenReturn(_exchangeId); + when(exchangeRecord.getType()).thenReturn(Exchange.class.getSimpleName()); + when(_exchange.asObjectRecord()).thenReturn(exchangeRecord); + _bindingArgs = new HashMap<String, Object>(); String argKey = AMQPFilterTypes.JMS_SELECTOR.toString(); String argValue = "some selector expression"; @@ -124,8 +134,8 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest DurableConfigurationStoreHelper.createExchange(_configStore, exchange); reopenStore(); - verify(_recoveryHandler).configuredObject(eq(_exchangeId), eq(EXCHANGE), - eq(map( org.apache.qpid.server.model.Exchange.NAME, getName(), + verify(_recoveryHandler).configuredObject(matchesRecord(_exchangeId, EXCHANGE, + map( org.apache.qpid.server.model.Exchange.NAME, getName(), org.apache.qpid.server.model.Exchange.TYPE, getName()+"Type", org.apache.qpid.server.model.Exchange.LIFETIME_POLICY, LifetimePolicy.DELETE_ON_NO_OUTBOUND_LINKS.name()))); } @@ -158,7 +168,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest DurableConfigurationStoreHelper.removeExchange(_configStore, exchange); reopenStore(); - verify(_recoveryHandler, never()).configuredObject(any(UUID.class), anyString(), anyMap()); + verify(_recoveryHandler, never()).configuredObject(any(ConfiguredObjectRecord.class)); } public void testBindQueue() throws Exception @@ -166,42 +176,86 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest AMQQueue queue = createTestQueue(QUEUE_NAME, "queueOwner", false, null); BindingImpl binding = new BindingImpl(UUIDGenerator.generateRandomUUID(), ROUTING_KEY, queue, _exchange, _bindingArgs); + DurableConfigurationStoreHelper.createQueue(_configStore, queue); DurableConfigurationStoreHelper.createBinding(_configStore, binding); reopenStore(); Map<String,Object> map = new HashMap<String, Object>(); - map.put(org.apache.qpid.server.model.Binding.EXCHANGE, _exchange.getId().toString()); - map.put(org.apache.qpid.server.model.Binding.QUEUE, queue.getId().toString()); - map.put(org.apache.qpid.server.model.Binding.NAME, ROUTING_KEY); - map.put(org.apache.qpid.server.model.Binding.ARGUMENTS,_bindingArgs); + map.put(Binding.NAME, ROUTING_KEY); + map.put(Binding.ARGUMENTS,_bindingArgs); + + Map<String,UUID> parents = new HashMap<String, UUID>(); + + parents.put(Exchange.class.getSimpleName(), _exchange.getId()); + parents.put(Queue.class.getSimpleName(), queue.getId()); + + verify(_recoveryHandler).configuredObject(matchesRecord(binding.getId(), BINDING, map, parents)); + } + + + private ConfiguredObjectRecord matchesRecord(UUID id, + String type, + Map<String, Object> attributes, + final Map<String, UUID> parents) + { + return argThat(new ConfiguredObjectMatcher(id, type, attributes, parents)); + } - verify(_recoveryHandler).configuredObject(eq(binding.getId()), eq(BINDING), - argThat(new IgnoreCreatedByMatcher(map))); + private ConfiguredObjectRecord matchesRecord(UUID id, String type, Map<String, Object> attributes) + { + return argThat(new ConfiguredObjectMatcher(id, type, attributes, ANY_MAP)); } - private static class IgnoreCreatedByMatcher extends ArgumentMatcher<Map<String,Object>> + private static class ConfiguredObjectMatcher extends ArgumentMatcher<ConfiguredObjectRecord> { private final Map<String,Object> _matchingMap; + private final UUID _id; + private final String _name; + private final Map<String,UUID> _parents; - private IgnoreCreatedByMatcher(final Map<String, Object> matchingMap) + private ConfiguredObjectMatcher(final UUID id, final String type, final Map<String, Object> matchingMap, Map<String,UUID> parents) { + _id = id; + _name = type; _matchingMap = matchingMap; + _parents = parents; } @Override public boolean matches(final Object argument) { - if(argument instanceof Map) + if(argument instanceof ConfiguredObjectRecord) { - Map<String,Object> arg = new HashMap<String, Object>((Map<String,Object>) argument); + ConfiguredObjectRecord binding = (ConfiguredObjectRecord) argument; + + Map<String,Object> arg = new HashMap<String, Object>(binding.getAttributes()); arg.remove("createdBy"); arg.remove("createdTime"); - return arg.equals(_matchingMap); - + return (_id == ANY_UUID || _id.equals(binding.getId())) + && _name.equals(binding.getType()) + && (_matchingMap == ANY_MAP || arg.equals(_matchingMap)) + && (_parents == ANY_MAP || matchesParents(binding)); } return false; } + + private boolean matchesParents(ConfiguredObjectRecord binding) + { + Map<String, ConfiguredObjectRecord> bindingParents = binding.getParents(); + if(bindingParents.size() != _parents.size()) + { + return false; + } + for(Map.Entry<String,UUID> entry : _parents.entrySet()) + { + if(!bindingParents.get(entry.getKey()).getId().equals(entry.getValue())) + { + return false; + } + } + return true; + } } public void testUnbindQueue() throws Exception @@ -214,9 +268,8 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest DurableConfigurationStoreHelper.removeBinding(_configStore, binding); reopenStore(); - verify(_recoveryHandler, never()).configuredObject(any(UUID.class), - eq(BINDING), - anyMap()); + verify(_recoveryHandler, never()).configuredObject(matchesRecord(ANY_UUID, BINDING, + ANY_MAP)); } public void testCreateQueueAMQQueue() throws Exception @@ -229,7 +282,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.put(Queue.NAME, getName()); queueAttributes.put(Queue.OWNER, getName()+"Owner"); queueAttributes.put(Queue.EXCLUSIVE, ExclusivityPolicy.CONTAINER.name()); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } public void testCreateQueueAMQQueueFieldTable() throws Exception @@ -251,7 +304,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.put(Queue.EXCLUSIVE, ExclusivityPolicy.CONTAINER.name()); queueAttributes.putAll(attributes); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } public void testCreateQueueAMQQueueWithAlternateExchange() throws Exception @@ -269,7 +322,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.put(Queue.EXCLUSIVE, ExclusivityPolicy.CONTAINER.name()); queueAttributes.put(Queue.ALTERNATE_EXCHANGE, alternateExchange.getId().toString()); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } private ExchangeImpl createTestAlternateExchange() @@ -302,7 +355,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.put(Queue.NAME, getName()); queueAttributes.putAll(attributes); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } @@ -329,7 +382,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest queueAttributes.putAll(attributes); queueAttributes.put(Queue.ALTERNATE_EXCHANGE, alternateExchange.getId().toString()); - verify(_recoveryHandler).configuredObject(eq(_queueId), eq(QUEUE), eq(queueAttributes)); + verify(_recoveryHandler).configuredObject(matchesRecord(_queueId, QUEUE, queueAttributes)); } public void testRemoveQueue() throws Exception @@ -344,9 +397,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest // remove queue DurableConfigurationStoreHelper.removeQueue(_configStore,queue); reopenStore(); - verify(_recoveryHandler, never()).configuredObject(any(UUID.class), - eq(org.apache.qpid.server.model.Queue.class.getName()), - anyMap()); + verify(_recoveryHandler, never()).configuredObject(any(ConfiguredObjectRecord.class)); } private AMQQueue createTestQueue(String queueName, @@ -399,6 +450,12 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest }); when(queue.getActualAttributes()).thenReturn(attributes); + + ConfiguredObjectRecord objectRecord = mock(ConfiguredObjectRecord.class); + when(objectRecord.getId()).thenReturn(_queueId); + when(objectRecord.getType()).thenReturn(Queue.class.getSimpleName()); + when(objectRecord.getAttributes()).thenReturn(attributes); + when(queue.asObjectRecord()).thenReturn(objectRecord); return queue; } @@ -415,6 +472,13 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest when(exchange.getTypeName()).thenReturn(getName() + "Type"); when(exchange.isAutoDelete()).thenReturn(true); when(exchange.getId()).thenReturn(_exchangeId); + ConfiguredObjectRecord exchangeRecord = mock(ConfiguredObjectRecord.class); + when(exchangeRecord.getId()).thenReturn(_exchangeId); + when(exchangeRecord.getType()).thenReturn(Exchange.class.getSimpleName()); + Map<String,Object> actualAttributesExceptId = new HashMap<String, Object>(actualAttributes); + actualAttributesExceptId.remove("id"); + when(exchangeRecord.getAttributes()).thenReturn(actualAttributesExceptId); + when(exchange.asObjectRecord()).thenReturn(exchangeRecord); return exchange; } @@ -425,7 +489,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest _configStore = createConfigStore(); _configStore.openConfigurationStore("testName", _configurationStoreSettings); - _configStore.recoverConfigurationStore(_recoveryHandler); + _configStore.recoverConfigurationStore(mock(ConfiguredObject.class), _recoveryHandler); } protected abstract DurableConfigurationStore createConfigStore() throws Exception; diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/JsonFileConfigStoreTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/JsonFileConfigStoreTest.java index 5d2998de86..bca16b6e70 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/JsonFileConfigStoreTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/JsonFileConfigStoreTest.java @@ -27,16 +27,19 @@ import java.util.Map; import java.util.UUID; import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.util.ServerScopedRuntimeException; import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.test.utils.TestFileUtils; import org.apache.qpid.util.FileUtils; +import org.mockito.ArgumentMatcher; import org.mockito.InOrder; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -53,6 +56,10 @@ public class JsonFileConfigStoreTest extends QpidTestCase private String _virtualHostName; private File _storeLocation; + + private static final UUID ANY_UUID = UUID.randomUUID(); + private static final Map<String, Object> ANY_MAP = new HashMap<String, Object>(); + @Override public void setUp() throws Exception { @@ -110,10 +117,10 @@ public class JsonFileConfigStoreTest extends QpidTestCase public void testStartFromNoStore() throws Exception { _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); - _store.recoverConfigurationStore(_recoveryHandler); + _store.recoverConfigurationStore(mock(ConfiguredObject.class), _recoveryHandler); InOrder inorder = inOrder(_recoveryHandler); inorder.verify(_recoveryHandler).beginConfigurationRecovery(eq(_store), eq(0)); - inorder.verify(_recoveryHandler,never()).configuredObject(any(UUID.class),anyString(),anyMap()); + inorder.verify(_recoveryHandler,never()).configuredObject(any(ConfiguredObjectRecord.class)); inorder.verify(_recoveryHandler).completeConfigurationRecovery(); _store.closeConfigurationStore(); } @@ -124,11 +131,11 @@ public class JsonFileConfigStoreTest extends QpidTestCase when(_recoveryHandler.completeConfigurationRecovery()).thenReturn(NEW_CONFIG_VERSION); _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); - _store.recoverConfigurationStore(_recoveryHandler); + _store.recoverConfigurationStore(mock(ConfiguredObject.class), _recoveryHandler); _store.closeConfigurationStore(); _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); - _store.recoverConfigurationStore(_recoveryHandler); + _store.recoverConfigurationStore(mock(ConfiguredObject.class), _recoveryHandler); InOrder inorder = inOrder(_recoveryHandler); // first time the config version should be the initial version - 0 @@ -147,12 +154,12 @@ public class JsonFileConfigStoreTest extends QpidTestCase final String queueType = Queue.class.getSimpleName(); final Map<String,Object> queueAttr = Collections.singletonMap("name", (Object) "q1"); - _store.create(queueId, queueType, queueAttr); + _store.create(new ConfiguredObjectRecordImpl(queueId, queueType, queueAttr)); _store.closeConfigurationStore(); _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); - _store.recoverConfigurationStore(_recoveryHandler); - verify(_recoveryHandler).configuredObject(eq(queueId), eq(queueType), eq(queueAttr)); + _store.recoverConfigurationStore(mock(ConfiguredObject.class), _recoveryHandler); + verify(_recoveryHandler).configuredObject(matchesRecord(queueId, queueType, queueAttr)); _store.closeConfigurationStore(); } @@ -163,18 +170,18 @@ public class JsonFileConfigStoreTest extends QpidTestCase final String queueType = Queue.class.getSimpleName(); Map<String,Object> queueAttr = Collections.singletonMap("name", (Object) "q1"); - _store.create(queueId, queueType, queueAttr); + _store.create(new ConfiguredObjectRecordImpl(queueId, queueType, queueAttr)); queueAttr = new HashMap<String,Object>(queueAttr); queueAttr.put("owner", "theowner"); - _store.update(queueId, queueType, queueAttr); + _store.update(false, new ConfiguredObjectRecordImpl(queueId, queueType, queueAttr)); _store.closeConfigurationStore(); _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); - _store.recoverConfigurationStore(_recoveryHandler); - verify(_recoveryHandler).configuredObject(eq(queueId), eq(queueType), eq(queueAttr)); + _store.recoverConfigurationStore(mock(ConfiguredObject.class), _recoveryHandler); + verify(_recoveryHandler).configuredObject(matchesRecord(queueId, queueType, queueAttr)); _store.closeConfigurationStore(); } @@ -186,16 +193,17 @@ public class JsonFileConfigStoreTest extends QpidTestCase final String queueType = Queue.class.getSimpleName(); Map<String,Object> queueAttr = Collections.singletonMap("name", (Object) "q1"); - _store.create(queueId, queueType, queueAttr); + final ConfiguredObjectRecordImpl record = new ConfiguredObjectRecordImpl(queueId, queueType, queueAttr); + _store.create(record); - _store.remove(queueId, queueType); + _store.remove(record); _store.closeConfigurationStore(); _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); - _store.recoverConfigurationStore(_recoveryHandler); - verify(_recoveryHandler, never()).configuredObject(any(UUID.class), anyString(), anyMap()); + _store.recoverConfigurationStore(mock(ConfiguredObject.class), _recoveryHandler); + verify(_recoveryHandler, never()).configuredObject(any(ConfiguredObjectRecord.class)); _store.closeConfigurationStore(); } @@ -204,7 +212,7 @@ public class JsonFileConfigStoreTest extends QpidTestCase _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); try { - _store.create(UUID.randomUUID(), "wibble", Collections.<String, Object>emptyMap()); + _store.create(new ConfiguredObjectRecordImpl(UUID.randomUUID(), "wibble", Collections.<String, Object>emptyMap())); fail("Should not be able to create instance of type wibble"); } catch (StoreException e) @@ -217,10 +225,10 @@ public class JsonFileConfigStoreTest extends QpidTestCase { _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); final UUID id = UUID.randomUUID(); - _store.create(id, "Queue", Collections.<String, Object>emptyMap()); + _store.create(new ConfiguredObjectRecordImpl(id, "Queue", Collections.<String, Object>emptyMap())); try { - _store.create(id, "Exchange", Collections.<String, Object>emptyMap()); + _store.create(new ConfiguredObjectRecordImpl(id, "Exchange", Collections.<String, Object>emptyMap())); fail("Should not be able to create two objects with same id"); } catch (StoreException e) @@ -234,13 +242,13 @@ public class JsonFileConfigStoreTest extends QpidTestCase { _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); final UUID id = UUID.randomUUID(); - _store.create(id, "Queue", Collections.<String, Object>emptyMap()); + _store.create(new ConfiguredObjectRecordImpl(id, "Queue", Collections.<String, Object>emptyMap())); _store.closeConfigurationStore(); _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); try { - _store.update(id, "Exchange", Collections.<String, Object>emptyMap()); + _store.update(false, new ConfiguredObjectRecordImpl(id, "Exchange", Collections.<String, Object>emptyMap())); fail("Should not be able to update object to different type"); } catch (StoreException e) @@ -279,32 +287,76 @@ public class JsonFileConfigStoreTest extends QpidTestCase final Map<String, Object> EMPTY_ATTR = Collections.emptyMap(); final UUID exchangeId = new UUID(0, 2); - final Map<String, Object> bindingAttributes = new HashMap<String, Object>(); - bindingAttributes.put(Binding.EXCHANGE, exchangeId); - bindingAttributes.put(Binding.QUEUE, queueId); - final Map<String, Object> binding2Attributes = new HashMap<String, Object>(); - binding2Attributes.put(Binding.EXCHANGE, exchangeId); - binding2Attributes.put(Binding.QUEUE, queue2Id); final UUID bindingId = new UUID(0, 3); final UUID binding2Id = new UUID(1, 3); - _store.create(queueId, "Queue", EMPTY_ATTR); - _store.create(queue2Id, "Queue", EMPTY_ATTR); - _store.create(exchangeId, "Exchange", EMPTY_ATTR); - _store.update(true, - new ConfiguredObjectRecord(bindingId, "Binding", bindingAttributes), - new ConfiguredObjectRecord(binding2Id, "Binding", binding2Attributes)); + final ConfiguredObjectRecordImpl queueRecord = new ConfiguredObjectRecordImpl(queueId, "Queue", EMPTY_ATTR); + _store.create(queueRecord); + final ConfiguredObjectRecordImpl queue2Record = new ConfiguredObjectRecordImpl(queue2Id, "Queue", EMPTY_ATTR); + _store.create(queue2Record); + final ConfiguredObjectRecordImpl exchangeRecord = new ConfiguredObjectRecordImpl(exchangeId, "Exchange", EMPTY_ATTR); + _store.create(exchangeRecord); + Map<String,ConfiguredObjectRecord> bindingParents = new HashMap<String, ConfiguredObjectRecord>(); + bindingParents.put("Exchange", exchangeRecord); + bindingParents.put("Queue", queueRecord); + final ConfiguredObjectRecordImpl bindingRecord = + new ConfiguredObjectRecordImpl(bindingId, "Binding", EMPTY_ATTR, bindingParents); + + + Map<String,ConfiguredObjectRecord> binding2Parents = new HashMap<String, ConfiguredObjectRecord>(); + binding2Parents.put("Exchange", exchangeRecord); + binding2Parents.put("Queue", queue2Record); + final ConfiguredObjectRecordImpl binding2Record = + new ConfiguredObjectRecordImpl(binding2Id, "Binding", EMPTY_ATTR, binding2Parents); + _store.update(true, bindingRecord, binding2Record); _store.closeConfigurationStore(); _store.openConfigurationStore(_virtualHostName, _configurationStoreSettings); - _store.recoverConfigurationStore(_recoveryHandler); - verify(_recoveryHandler).configuredObject(eq(queueId), eq("Queue"), eq(EMPTY_ATTR)); - verify(_recoveryHandler).configuredObject(eq(queue2Id), eq("Queue"), eq(EMPTY_ATTR)); - verify(_recoveryHandler).configuredObject(eq(exchangeId), eq("Exchange"), eq(EMPTY_ATTR)); - verify(_recoveryHandler).configuredObject(eq(bindingId),eq("Binding"), eq(bindingAttributes)); - verify(_recoveryHandler).configuredObject(eq(binding2Id),eq("Binding"), eq(binding2Attributes)); + _store.recoverConfigurationStore(mock(ConfiguredObject.class), _recoveryHandler); + verify(_recoveryHandler).configuredObject(matchesRecord(queueId, "Queue", EMPTY_ATTR)); + verify(_recoveryHandler).configuredObject(matchesRecord(queue2Id, "Queue", EMPTY_ATTR)); + verify(_recoveryHandler).configuredObject(matchesRecord(exchangeId, "Exchange", EMPTY_ATTR)); + verify(_recoveryHandler).configuredObject(matchesRecord(bindingId, "Binding", EMPTY_ATTR)); + verify(_recoveryHandler).configuredObject(matchesRecord(binding2Id, "Binding", EMPTY_ATTR)); _store.closeConfigurationStore(); } + private ConfiguredObjectRecord matchesRecord(UUID id, String type, Map<String, Object> attributes) + { + return argThat(new ConfiguredObjectMatcher(id, type, attributes)); + } + + private static class ConfiguredObjectMatcher extends ArgumentMatcher<ConfiguredObjectRecord> + { + private final Map<String,Object> _matchingMap; + private final UUID _id; + private final String _name; + + private ConfiguredObjectMatcher(final UUID id, final String type, final Map<String, Object> matchingMap) + { + _id = id; + _name = type; + _matchingMap = matchingMap; + } + + @Override + public boolean matches(final Object argument) + { + if(argument instanceof ConfiguredObjectRecord) + { + ConfiguredObjectRecord binding = (ConfiguredObjectRecord) argument; + + Map<String,Object> arg = new HashMap<String, Object>(binding.getAttributes()); + arg.remove("createdBy"); + arg.remove("createdTime"); + return (_id == ANY_UUID || _id.equals(binding.getId())) + && _name.equals(binding.getType()) + && (_matchingMap == ANY_MAP || arg.equals(_matchingMap)); + + } + return false; + } + } + } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java index e46a8939f4..fc69a53c85 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java @@ -32,6 +32,8 @@ import java.util.UUID; import org.apache.log4j.Logger; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler; import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.util.FileUtils; @@ -64,11 +66,11 @@ public abstract class MessageStoreQuotaEventsTestBase extends QpidTestCase imple Map<String, Object> storeSettings = createStoreSettings(_storeLocation.getAbsolutePath()); _store = createStore(); - ((DurableConfigurationStore)_store).openConfigurationStore("test", storeSettings); + MessageStoreRecoveryHandler recoveryHandler = mock(MessageStoreRecoveryHandler.class); when(recoveryHandler.begin()).thenReturn(mock(StoredMessageRecoveryHandler.class)); _store.openMessageStore("test", storeSettings); - _store.recoverMessageStore(recoveryHandler, null); + _store.recoverMessageStore(mock(ConfiguredObject.class), recoveryHandler, null); _transactionResource = UUID.randomUUID(); _events = new ArrayList<Event>(); diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreTestCase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreTestCase.java index 45f7a2a39e..1d95133784 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreTestCase.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreTestCase.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.UUID; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler; import org.apache.qpid.server.store.Transaction.Record; @@ -65,7 +66,7 @@ public abstract class MessageStoreTestCase extends QpidTestCase _store = createMessageStore(); _store.openMessageStore("test", _storeSettings); - _store.recoverMessageStore(_messageStoreRecoveryHandler, _logRecoveryHandler); + _store.recoverMessageStore(mock(ConfiguredObject.class), _messageStoreRecoveryHandler, _logRecoveryHandler); } protected abstract Map<String, Object> getStoreSettings() throws Exception; @@ -106,7 +107,7 @@ public abstract class MessageStoreTestCase extends QpidTestCase _store = createMessageStore(); _store.openMessageStore("test", _storeSettings); - _store.recoverMessageStore(_messageStoreRecoveryHandler, _logRecoveryHandler); + _store.recoverMessageStore(mock(ConfiguredObject.class), _messageStoreRecoveryHandler, _logRecoveryHandler); } private Record getTestRecord(long messageNumber) { diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java index e3d56ab4bb..c90c6af220 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/DurableConfigurationRecovererTest.java @@ -30,6 +30,7 @@ import java.util.UUID; import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.logging.EventLogger; +import org.apache.qpid.server.store.ConfiguredObjectRecordImpl; import org.apache.qpid.server.store.StoreException; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.exchange.AMQUnknownExchangeType; @@ -229,17 +230,16 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 0); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "org.apache.qpid.server.model.Binding", createBinding("key", - DIRECT_EXCHANGE_ID, - QUEUE_ID, "x-filter-jms-selector", - "wibble")); + "wibble"), + createBindingParents(DIRECT_EXCHANGE_ID, QUEUE_ID))); final ConfiguredObjectRecord[] expected = { - new ConfiguredObjectRecord(new UUID(1, 0), "Binding", - createBinding("key", DIRECT_EXCHANGE_ID, QUEUE_ID)) + new ConfiguredObjectRecordImpl(new UUID(1, 0), "Binding", + createBinding("key")) }; verifyCorrectUpdates(expected); @@ -254,32 +254,30 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 0); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "org.apache.qpid.server.model.Binding", createBinding("key", - DIRECT_EXCHANGE_ID, - QUEUE_ID, "x-filter-jms-selector", "wibble", "not-a-selector", - "moo")); + "moo"), + createBindingParents(DIRECT_EXCHANGE_ID, QUEUE_ID))); final UUID customExchangeId = new UUID(3,0); - _durableConfigurationRecoverer.configuredObject(new UUID(2, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(2, 0), "org.apache.qpid.server.model.Binding", createBinding("key", - customExchangeId, - QUEUE_ID, "x-filter-jms-selector", "wibble", "not-a-selector", - "moo")); + "moo"), + createBindingParents(customExchangeId,QUEUE_ID))); - _durableConfigurationRecoverer.configuredObject(customExchangeId, + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(customExchangeId, "org.apache.qpid.server.model.Exchange", - createExchange(CUSTOM_EXCHANGE_NAME, HeadersExchange.TYPE)); + createExchange(CUSTOM_EXCHANGE_NAME, HeadersExchange.TYPE))); final ExchangeImpl customExchange = mock(ExchangeImpl.class); @@ -323,10 +321,10 @@ public class DurableConfigurationRecovererTest extends QpidTestCase final ConfiguredObjectRecord[] expected = { - new ConfiguredObjectRecord(new UUID(1, 0), "org.apache.qpid.server.model.Binding", - createBinding("key", DIRECT_EXCHANGE_ID, QUEUE_ID, "not-a-selector", "moo")), - new ConfiguredObjectRecord(new UUID(2, 0), "org.apache.qpid.server.model.Binding", - createBinding("key", customExchangeId, QUEUE_ID, "not-a-selector", "moo")) + new ConfiguredObjectRecordImpl(new UUID(1, 0), "org.apache.qpid.server.model.Binding", + createBinding("key", "not-a-selector", "moo")), + new ConfiguredObjectRecordImpl(new UUID(2, 0), "org.apache.qpid.server.model.Binding", + createBinding("key", "not-a-selector", "moo")) }; verifyCorrectUpdates(expected); @@ -340,17 +338,16 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 0); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "org.apache.qpid.server.model.Binding", createBinding("key", - TOPIC_EXCHANGE_ID, - QUEUE_ID, "x-filter-jms-selector", - "wibble")); + "wibble"), + createBindingParents(TOPIC_EXCHANGE_ID,QUEUE_ID))); final ConfiguredObjectRecord[] expected = { - new ConfiguredObjectRecord(new UUID(1, 0), "Binding", - createBinding("key", TOPIC_EXCHANGE_ID, QUEUE_ID, "x-filter-jms-selector", "wibble")) + new ConfiguredObjectRecordImpl(new UUID(1, 0), "Binding", + createBinding("key", "x-filter-jms-selector", "wibble")) }; verifyCorrectUpdates(expected); @@ -363,16 +360,15 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 2); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "Binding", createBinding("key", - DIRECT_EXCHANGE_ID, - QUEUE_ID, "x-filter-jms-selector", - "wibble")); + "wibble"), + createBindingParents(DIRECT_EXCHANGE_ID,QUEUE_ID))); doThrow(new RuntimeException("Update Should not be called")) - .when(_store).update(anyBoolean(), any(ConfiguredObjectRecord[].class)); + .when(_store).update(anyBoolean(), any(ConfiguredObjectRecordImpl[].class)); _durableConfigurationRecoverer.completeConfigurationRecovery(); } @@ -382,13 +378,13 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 2); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), "Binding", createBinding("key", - new UUID(3,0), - QUEUE_ID, "x-filter-jms-selector", - "wibble")); + "wibble"), + createBindingParents(new UUID(3,0), + QUEUE_ID))); try { @@ -410,8 +406,8 @@ public class DurableConfigurationRecovererTest extends QpidTestCase try { final Map<String, Object> emptyArguments = Collections.emptyMap(); - _durableConfigurationRecoverer.configuredObject(new UUID(1, 0), - "Wibble", emptyArguments); + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(new UUID(1, 0), + "Wibble", emptyArguments)); _durableConfigurationRecoverer.completeConfigurationRecovery(); fail("Expected resolution to fail due to unknown object type"); } @@ -474,11 +470,11 @@ public class DurableConfigurationRecovererTest extends QpidTestCase _durableConfigurationRecoverer.beginConfigurationRecovery(_store, 2); - _durableConfigurationRecoverer.configuredObject(queueId, Queue.class.getSimpleName(), - createQueue("testQueue", exchangeId)); - _durableConfigurationRecoverer.configuredObject(exchangeId, + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(queueId, Queue.class.getSimpleName(), + createQueue("testQueue", exchangeId))); + _durableConfigurationRecoverer.configuredObject(new ConfiguredObjectRecordImpl(exchangeId, org.apache.qpid.server.model.Exchange.class.getSimpleName(), - createExchange(CUSTOM_EXCHANGE_NAME, HeadersExchange.TYPE)); + createExchange(CUSTOM_EXCHANGE_NAME, HeadersExchange.TYPE))); _durableConfigurationRecoverer.completeConfigurationRecovery(); @@ -499,16 +495,14 @@ public class DurableConfigurationRecovererTest extends QpidTestCase return null; } - }).when(_store).update(anyBoolean(), any(ConfiguredObjectRecord[].class)); + }).when(_store).update(anyBoolean(), any(ConfiguredObjectRecordImpl[].class)); } - private Map<String,Object> createBinding(String bindingKey, UUID exchangeId, UUID queueId, String... args) + private Map<String,Object> createBinding(String bindingKey, String... args) { Map<String, Object> binding = new LinkedHashMap<String, Object>(); binding.put("name", bindingKey); - binding.put(Binding.EXCHANGE, exchangeId.toString()); - binding.put(Binding.QUEUE, queueId.toString()); Map<String,String> argumentMap = new LinkedHashMap<String, String>(); if(args != null && args.length != 0) { @@ -530,6 +524,15 @@ public class DurableConfigurationRecovererTest extends QpidTestCase return binding; } + private Map<String,ConfiguredObjectRecord> createBindingParents(UUID exchangeId, UUID queueId) + { + Map<String,ConfiguredObjectRecord> parents = new HashMap<String, ConfiguredObjectRecord>(); + parents.put("Exchange", new ConfiguredObjectRecordImpl(exchangeId,"Exchange",Collections.<String,Object>emptyMap())); + parents.put("Queue", new ConfiguredObjectRecordImpl(queueId,"Queue",Collections.<String,Object>emptyMap())); + + return parents; + } + private Map<String, Object> createExchange(String name, ExchangeType<HeadersExchange> type) { diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java index 3a36ddef2c..072bd6a87f 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlTest.java @@ -20,7 +20,9 @@ */ package org.apache.qpid.server.security.access.plugins; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -181,7 +183,7 @@ public class DefaultAccessControlTest extends TestCase final RuleSet rs = new RuleSet(mock(EventLoggerProvider.class)); // grant user4 access right on any method in any component - rs.grant(1, "user4", Permission.ALLOW, Operation.ACCESS, ObjectType.METHOD, new ObjectProperties(ObjectProperties.STAR)); + rs.grant(1, "user4", Permission.ALLOW, Operation.ACCESS, ObjectType.METHOD, new ObjectProperties(ObjectProperties.WILD_CARD)); configureAccessControl(rs); Subject.doAs(TestPrincipalUtils.createTestSubject("user4"), new PrivilegedAction<Object>() { @@ -207,7 +209,7 @@ public class DefaultAccessControlTest extends TestCase final RuleSet rs = new RuleSet(mock(EventLoggerProvider.class)); // grant user5 access right on any methods in "Test" component - ObjectProperties ruleProperties = new ObjectProperties(ObjectProperties.STAR); + ObjectProperties ruleProperties = new ObjectProperties(ObjectProperties.WILD_CARD); ruleProperties.put(ObjectProperties.Property.COMPONENT, "Test"); rs.grant(1, "user5", Permission.ALLOW, Operation.ACCESS, ObjectType.METHOD, ruleProperties); configureAccessControl(rs); @@ -234,6 +236,7 @@ public class DefaultAccessControlTest extends TestCase public void testAccess() throws Exception { final Subject subject = TestPrincipalUtils.createTestSubject("user1"); + final String testVirtualHost = getName(); final InetAddress inetAddress = InetAddress.getLocalHost(); final InetSocketAddress inetSocketAddress = new InetSocketAddress(inetAddress, 1); @@ -249,13 +252,12 @@ public class DefaultAccessControlTest extends TestCase { RuleSet mockRuleSet = mock(RuleSet.class); - - DefaultAccessControl accessControl = new DefaultAccessControl(mockRuleSet); - accessControl.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + ObjectProperties properties = new ObjectProperties(testVirtualHost); + accessControl.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, properties); - verify(mockRuleSet).check(subject, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY, inetAddress); + verify(mockRuleSet).check(subject, Operation.ACCESS, ObjectType.VIRTUALHOST, properties, inetAddress); return null; } }); diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java index caf9b2fb61..32037807cd 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java @@ -21,24 +21,26 @@ package org.apache.qpid.server.security.access.plugins; -import java.security.Principal; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import javax.security.auth.Subject; -import org.apache.qpid.server.logging.EventLogger; +import org.apache.qpid.server.exchange.ExchangeImpl; import org.apache.qpid.server.logging.EventLoggerProvider; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.security.Result; import org.apache.qpid.server.security.access.ObjectProperties; import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.Permission; +import org.apache.qpid.server.security.access.ObjectProperties.Property; import org.apache.qpid.server.security.access.config.Rule; import org.apache.qpid.server.security.access.config.RuleSet; import org.apache.qpid.server.security.auth.TestPrincipalUtils; import org.apache.qpid.test.utils.QpidTestCase; -import static org.mockito.Mockito.mock; - /** * This test checks that the {@link RuleSet} object which forms the core of the access control plugin performs correctly. * @@ -51,6 +53,9 @@ import static org.mockito.Mockito.mock; */ public class RuleSetTest extends QpidTestCase { + private static final String DENIED_VH = "deniedVH"; + private static final String ALLOWED_VH = "allowedVH"; + private RuleSet _ruleSet; // Object under test private static final String TEST_USER = "user"; @@ -60,6 +65,8 @@ public class RuleSetTest extends QpidTestCase private String _exchangeName = "amq.direct"; private String _exchangeType = "direct"; private Subject _testSubject = TestPrincipalUtils.createTestSubject(TEST_USER); + private AMQQueue<?> _queue; + private VirtualHost<?> _virtualHost; @Override public void setUp() throws Exception @@ -67,6 +74,11 @@ public class RuleSetTest extends QpidTestCase super.setUp(); _ruleSet = new RuleSet(mock(EventLoggerProvider.class)); + + _virtualHost = mock(VirtualHost.class); + _queue = mock(AMQQueue.class); + when(_queue.getName()).thenReturn(_queueName); + when(_queue.getParent(VirtualHost.class)).thenReturn(_virtualHost); } @Override @@ -83,10 +95,8 @@ public class RuleSetTest extends QpidTestCase public void assertDenyGrantAllow(Subject subject, Operation operation, ObjectType objectType, ObjectProperties properties) { - final Principal identity = subject.getPrincipals().iterator().next(); - assertEquals(Result.DENIED, _ruleSet.check(subject, operation, objectType, properties)); - _ruleSet.grant(0, identity.getName(), Permission.ALLOW, operation, objectType, properties); + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, operation, objectType, properties); assertEquals(1, _ruleSet.getRuleCount()); assertEquals(Result.ALLOWED, _ruleSet.check(subject, operation, objectType, properties)); } @@ -98,17 +108,77 @@ public class RuleSetTest extends QpidTestCase assertEquals(_ruleSet.getDefault(), _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } - public void testVirtualHostAccess() throws Exception + public void testVirtualHostAccessAllowPermissionWithVirtualHostName() throws Exception { - assertDenyGrantAllow(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST); + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH)); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); } + public void testVirtualHostAccessAllowPermissionWithNameSetToWildCard() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ObjectProperties.WILD_CARD)); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessAllowPermissionWithNoName() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessDenyPermissionWithNoName() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessDenyPermissionWithNameSetToWildCard() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ObjectProperties.WILD_CARD)); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessAllowDenyPermissions() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH)); + _ruleSet.grant(1, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH)); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(ALLOWED_VH))); + assertEquals(Result.DENIED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testVirtualHostAccessAllowPermissionWithVirtualHostNameOtherPredicate() throws Exception + { + ObjectProperties properties = new ObjectProperties(); + properties.put(Property.VIRTUALHOST_NAME, ALLOWED_VH); + + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, properties); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, properties)); + assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.ACCESS, ObjectType.VIRTUALHOST, new ObjectProperties(DENIED_VH))); + } + + public void testQueueCreateNamed() throws Exception { assertDenyGrantAllow(_testSubject, Operation.CREATE, ObjectType.QUEUE, new ObjectProperties(_queueName)); } - public void testQueueCreatenamedNullRoutingKey() + public void testQueueCreateNamedVirtualHost() throws Exception + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.QUEUE, new ObjectProperties(Property.VIRTUALHOST_NAME, ALLOWED_VH)); + + when(_virtualHost.getName()).thenReturn(ALLOWED_VH); + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, new ObjectProperties(_queue))); + + when(_virtualHost.getName()).thenReturn(DENIED_VH); + assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.QUEUE, new ObjectProperties(_queue))); + } + + public void testQueueCreateNamedNullRoutingKey() { ObjectProperties properties = new ObjectProperties(_queueName); properties.put(ObjectProperties.Property.ROUTING_KEY, (String) null); @@ -116,6 +186,21 @@ public class RuleSetTest extends QpidTestCase assertDenyGrantAllow(_testSubject, Operation.CREATE, ObjectType.QUEUE, properties); } + public void testExchangeCreateNamedVirtualHost() + { + _ruleSet.grant(0, TEST_USER, Permission.ALLOW, Operation.CREATE, ObjectType.EXCHANGE, new ObjectProperties(Property.VIRTUALHOST_NAME, ALLOWED_VH)); + + ExchangeImpl<?> exchange = mock(ExchangeImpl.class); + when(exchange.getParent(VirtualHost.class)).thenReturn(_virtualHost); + when(exchange.getTypeName()).thenReturn(_exchangeType); + when(_virtualHost.getName()).thenReturn(ALLOWED_VH); + + assertEquals(Result.ALLOWED, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.EXCHANGE, new ObjectProperties(exchange))); + + when(_virtualHost.getName()).thenReturn(DENIED_VH); + assertEquals(Result.DEFER, _ruleSet.check(_testSubject, Operation.CREATE, ObjectType.EXCHANGE, new ObjectProperties(exchange))); + } + public void testExchangeCreate() { ObjectProperties properties = new ObjectProperties(_exchangeName); diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java index 040be92ceb..999da2da6c 100644 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java @@ -291,7 +291,7 @@ public class ServerSessionDelegate extends SessionDelegate final VirtualHost virtualHost = getVirtualHost(ssn); try { - virtualHost.getSecurityManager().authorisePublish(messageMetaData.isImmediate(), messageMetaData.getRoutingKey(), exchange.getName()); + virtualHost.getSecurityManager().authorisePublish(messageMetaData.isImmediate(), messageMetaData.getRoutingKey(), exchange.getName(), virtualHost.getName()); } catch (AccessControlException e) { diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java index baf5eceef7..7bde83cc99 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java @@ -303,9 +303,10 @@ public class AMQChannel<T extends AMQProtocolSession<T>> public void setPublishFrame(MessagePublishInfo info, final MessageDestination e) { String routingKey = info.getRoutingKey() == null ? null : info.getRoutingKey().asString(); - SecurityManager securityManager = getVirtualHost().getSecurityManager(); + VirtualHost virtualHost = getVirtualHost(); + SecurityManager securityManager = virtualHost.getSecurityManager(); - securityManager.authorisePublish(info.isImmediate(), routingKey, e.getName()); + securityManager.authorisePublish(info.isImmediate(), routingKey, e.getName(), virtualHost.getName()); _currentMessage = new IncomingMessage(info); _currentMessage.setMessageDestination(e); diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/ConnectionOpenMethodHandler.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/ConnectionOpenMethodHandler.java index a29d56605a..1a29806f62 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/ConnectionOpenMethodHandler.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/ConnectionOpenMethodHandler.java @@ -80,6 +80,8 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener<Con } else { + session.setVirtualHost(virtualHost); + // Check virtualhost access try { @@ -95,7 +97,6 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener<Con throw body.getConnectionException(AMQConstant.CONNECTION_FORCED, "Virtual host '" + virtualHost.getName() + "' is not active"); } - session.setVirtualHost(virtualHost); // See Spec (0.8.2). Section 3.1.2 Virtual Hosts if (session.getContextKey() == null) diff --git a/qpid/java/broker-plugins/jdbc-provider-bone/src/main/java/resources/virtualhost/store/pool/bonecp/add.html b/qpid/java/broker-plugins/jdbc-provider-bone/src/main/java/resources/virtualhost/store/pool/bonecp/add.html index 0a83bd9f6f..5b053849a3 100644 --- a/qpid/java/broker-plugins/jdbc-provider-bone/src/main/java/resources/virtualhost/store/pool/bonecp/add.html +++ b/qpid/java/broker-plugins/jdbc-provider-bone/src/main/java/resources/virtualhost/store/pool/bonecp/add.html @@ -20,7 +20,7 @@ <td class="tableContainer-labelCell" style="width: 300px;"><strong>Partition Count: </strong></td> <td class="tableContainer-valueCell"> <input data-dojo-type="dijit/form/NumberSpinner" id="formAddVirtualHost.specific.store.pool.parititions" - name="minConnectionsPerPartition" value="4" smallDelta="1" constraints="{min:1,max:1000,places:0}"/> + name="partitionCount" value="4" smallDelta="1" constraints="{min:1,max:1000,places:0}"/> </td> </tr> <tr> diff --git a/qpid/java/broker-plugins/management-http/pom.xml b/qpid/java/broker-plugins/management-http/pom.xml index 4bfaf5e5d2..afb295f7cd 100644 --- a/qpid/java/broker-plugins/management-http/pom.xml +++ b/qpid/java/broker-plugins/management-http/pom.xml @@ -69,6 +69,12 @@ <type>zip</type> </dependency> + <dependency> + <groupId>org.webjars</groupId> + <artifactId>cryptojs</artifactId> + <version>3.1.2</version> + </dependency> + <!-- test dependencies --> <dependency> <groupId>org.apache.qpid</groupId> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java index 618aaed319..72e9bac29b 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java @@ -89,6 +89,10 @@ public class FileServlet extends HttpServlet } URL resourceURL = getClass().getResource(_resourcePathPrefix + filename); + if(resourceURL == null) + { + resourceURL = getClass().getResource("/META-INF" + _resourcePathPrefix + filename); + } if(resourceURL != null) { response.setStatus(HttpServletResponse.SC_OK); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java index 0947ae2a89..b23f0cb168 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java @@ -485,10 +485,10 @@ public class MessageServlet extends AbstractServlet } - private void authorizeMethod(String methodName, VirtualHost host) + private void authorizeMethod(String methodName, VirtualHost<?> vhost) { - SecurityManager securityManager = host.getSecurityManager(); - securityManager.authoriseMethod(Operation.UPDATE, "VirtualHost.Queue", methodName); + SecurityManager securityManager = getBroker().getSecurityManager(); + securityManager.authoriseMethod(Operation.UPDATE, "VirtualHost.Queue", methodName, vhost.getName()); } } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java index 2ca67fadc9..af3973c7b3 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java @@ -59,7 +59,7 @@ public class SaslServlet extends AbstractServlet private static final String ATTR_ID = "SaslServlet.ID"; private static final String ATTR_SASL_SERVER = "SaslServlet.SaslServer"; private static final String ATTR_EXPIRY = "SaslServlet.Expiry"; - private static final long SASL_EXCHANGE_EXPIRY = 1000L; + private static final long SASL_EXCHANGE_EXPIRY = 3000L; public SaslServlet() { @@ -260,7 +260,17 @@ public class SaslServlet extends AbstractServlet session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); + if(challenge != null && challenge.length != 0) + { + Map<String, Object> outputObject = new LinkedHashMap<String, Object>(); + outputObject.put("challenge", new String(Base64.encodeBase64(challenge))); + + final PrintWriter writer = response.getWriter(); + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); + mapper.writeValue(writer, outputObject); + } response.setStatus(HttpServletResponse.SC_OK); } else diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js index 2d99f886ed..a25375a669 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js @@ -18,8 +18,8 @@ * under the License. * */ -define(["dojo/_base/xhr", "dojox/encoding/base64", "dojox/encoding/digests/_base", "dojox/encoding/digests/MD5"], - function (xhr, base64, digestsBase, MD5) { +define(["dojo/_base/xhr", "dojox/encoding/base64", "dojox/encoding/digests/_base", "dojox/encoding/digests/MD5", "dojox/uuid/generateRandomUuid", "dojo/request/script"], + function (xhr, base64, digestsBase, MD5, uuid, script) { var encodeUTF8 = function encodeUTF8(str) { var byteArray = []; @@ -83,34 +83,32 @@ var saslPlain = function saslPlain(user, password, callbackFunction) var saslCramMD5 = function saslCramMD5(user, password, saslMechanism, callbackFunction) { - - // Using dojo.xhrGet, as very little information is being sent - dojo.xhrPost({ - // The URL of the request - url: "rest/sasl", - content: { - mechanism: saslMechanism - }, - handleAs: "json", - failOk: true - }).then(function(data) - { - - var challengeBytes = base64.decode(data.challenge); - var wa=[]; - var bitLength = challengeBytes.length*8; - for(var i=0; i<bitLength; i+=8) + dojo.xhrPost({ + // The URL of the request + url: "rest/sasl", + content: { + mechanism: saslMechanism + }, + handleAs: "json", + failOk: true + }).then(function(data) { - wa[i>>5] |= (challengeBytes[i/8] & 0xFF)<<(i%32); - } - var challengeStr = digestsBase.wordToString(wa).substring(0,challengeBytes.length); - var digest = user + " " + MD5._hmac(challengeStr, password, digestsBase.outputTypes.Hex); - var id = data.id; + var challengeBytes = base64.decode(data.challenge); + var wa=[]; + var bitLength = challengeBytes.length*8; + for(var i=0; i<bitLength; i+=8) + { + wa[i>>5] |= (challengeBytes[i/8] & 0xFF)<<(i%32); + } + var challengeStr = digestsBase.wordToString(wa).substring(0,challengeBytes.length); + + var digest = user + " " + MD5._hmac(challengeStr, password, digestsBase.outputTypes.Hex); + var id = data.id; - var response = base64.encode(encodeUTF8( digest )); + var response = base64.encode(encodeUTF8( digest )); - dojo.xhrPost({ + dojo.xhrPost({ // The URL of the request url: "rest/sasl", content: { @@ -121,20 +119,163 @@ var saslCramMD5 = function saslCramMD5(user, password, saslMechanism, callbackFu failOk: true }).then(callbackFunction, errorHandler); - }, - function(error) - { - if(error.status == 403) + }, + function(error) { - alert("Authentication Failed"); - } - else - { - alert(error); - } - }); + if(error.status == 403) + { + alert("Authentication Failed"); + } + else + { + alert(error); + } + }); + + + }; + var saslScramSha1 = function saslScramSha1(user, password, saslMechanism, callbackFunction) + { + + script.get("webjars/cryptojs/3.1.2/rollups/hmac-sha1.js").then( function() + { + script.get("webjars/cryptojs/3.1.2/components/enc-base64-min.js").then ( function() + { + + var toBase64 = function toBase64( input ) + { + var result = []; + for(var i = 0; i < input.length; i++) + { + result[i] = input.charCodeAt(i); + } + return base64.encode( result ) + }; + + var fromBase64 = function fromBase64( input ) + { + var decoded = base64.decode( input ); + var result = ""; + for(var i = 0; i < decoded.length; i++) + { + result+= String.fromCharCode(decoded[i]); + } + return result; + }; + + var xor = function xor(lhs, rhs) { + var words = []; + for(var i = 0; i < lhs.words.length; i++) + { + words.push(lhs.words[i]^rhs.words[i]); + } + return CryptoJS.lib.WordArray.create(words); + }; + + var hasNonAscii = function hasNonAscii(name) { + for(var i = 0; i < name.length; i++) { + if(name.charCodeAt(i) > 127) { + return true; + } + } + return false; + }; + + var generateSaltedPassword = function generateSaltedPassword(salt, password, iterationCount) + { + var hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, password); + + hmac.update(salt); + hmac.update(CryptoJS.enc.Hex.parse("00000001")); + + var result = hmac.finalize(); + var previous = null; + for(var i = 1 ;i < iterationCount; i++) + { + hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA1, password); + hmac.update( previous != null ? previous : result ); + previous = hmac.finalize(); + result = xor(result, previous); + } + return result; + + }; + + GS2_HEADER = "n,,"; + + if(!hasNonAscii(user)) { + + user = user.replace(/=/g, "=3D"); + user = user.replace(/,/g, "=2C"); + + clientNonce = uuid(); + clientFirstMessageBare = "n=" + user + ",r=" + clientNonce; + dojo.xhrPost({ + // The URL of the request + url: "rest/sasl", + content: { + mechanism: saslMechanism, + response: toBase64(GS2_HEADER + clientFirstMessageBare) + }, + handleAs: "json", + failOk: true + }).then(function (data) { + var serverFirstMessage = fromBase64(data.challenge); + var id = data.id; + + var parts = serverFirstMessage.split(","); + nonce = parts[0].substring(2); + if (!nonce.substr(0, clientNonce.length) == clientNonce) { + alert("Authentication error - server nonce does not start with client nonce") + } + else { + var salt = CryptoJS.enc.Base64.parse(parts[1].substring(2)); + var iterationCount = parts[2].substring(2); + var saltedPassword = generateSaltedPassword(salt, password, iterationCount) + var clientFinalMessageWithoutProof = "c=" + toBase64(GS2_HEADER) + ",r=" + nonce; + var authMessage = clientFirstMessageBare + "," + serverFirstMessage + "," + clientFinalMessageWithoutProof; + var clientKey = CryptoJS.HmacSHA1("Client Key", saltedPassword); + var storedKey = CryptoJS.SHA1(clientKey); + var clientSignature = CryptoJS.HmacSHA1(authMessage, storedKey); + var clientProof = xor(clientKey, clientSignature); + var serverKey = CryptoJS.HmacSHA1("Server Key", saltedPassword); + serverSignature = CryptoJS.HmacSHA1(authMessage, serverKey); + dojo.xhrPost({ + // The URL of the request + url: "rest/sasl", + content: { + id: id, + response: toBase64(clientFinalMessageWithoutProof + + ",p=" + clientProof.toString(CryptoJS.enc.Base64)) + }, + handleAs: "json", + failOk: true + }).then(function (data) { + var serverFinalMessage = fromBase64(data.challenge); + if (serverSignature.toString(CryptoJS.enc.Base64) == serverFinalMessage.substring(2)) { + callbackFunction(); + } + else { + errorHandler("Server signature did not match"); + } + + + }, errorHandler); + } + + }, errorHandler); + } + else + { + alert("Username '"+name+"' is invalid"); + } + + }, errorHandler); + }, errorHandler); + }; + var containsMechanism = function containsMechanism(mechanisms, mech) { for (var i = 0; i < mechanisms.length; i++) { @@ -157,7 +298,11 @@ SaslClient.authenticate = function(username, password, callbackFunction) }).then(function(data) { var mechMap = data.mechanisms; - if (containsMechanism(mechMap, "CRAM-MD5")) + if(containsMechanism(mechMap, "SCRAM-SHA-1")) + { + saslScramSha1(username, password, "SCRAM-SHA-1", callbackFunction) + } + else if (containsMechanism(mechMap, "CRAM-MD5")) { saslCramMD5(username, password, "CRAM-MD5", callbackFunction); } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js index 3d349830ac..6d83a68fb4 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js @@ -140,7 +140,7 @@ define(["dojo/_base/xhr", util.isProviderManagingUsers = function(type) { - return (type === "PlainPasswordFile" || type === "Base64MD5PasswordFile"); + return (type === "PlainPasswordFile" || type === "Base64MD5PasswordFile" || type === "SCRAM-SHA1"); }; util.showSetAttributesDialog = function(attributeWidgetFactories, data, putURL, dialogTitle, appendNameToUrl) diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java index 7b0a48cac1..5a7674d4fd 100644 --- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java +++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java @@ -60,15 +60,15 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler private MBeanServer _mbs; private final boolean _managementRightsInferAllAccess; - private final Broker _broker; + private final Broker<?> _broker; - MBeanInvocationHandlerImpl(Broker broker) + MBeanInvocationHandlerImpl(Broker<?> broker) { _managementRightsInferAllAccess = Boolean.valueOf(System.getProperty(BrokerProperties.PROPERTY_MANAGEMENT_RIGHTS_INFER_ALL_ACCESS, "true")); _broker = broker; } - public static MBeanServerForwarder newProxyInstance(Broker broker) + public static MBeanServerForwarder newProxyInstance(Broker<?> broker) { final InvocationHandler handler = new MBeanInvocationHandlerImpl(broker); final Class<?>[] interfaces = new Class[] { MBeanServerForwarder.class }; @@ -195,28 +195,23 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler String methodName; // Get the component, type and impact, which may be null String type = getType(method, args); - String vhost = getVirtualHost(method, args); + String virtualHostName = getVirtualHost(method, args); int impact = getImpact(method, args); - // Get the security manager for the virtual host (if set) - SecurityManager security; - if (vhost == null) + if (virtualHostName != null) { - security = _broker.getSecurityManager(); - } - else - { - VirtualHost virtualHost = _broker.findVirtualHostByName(vhost); + VirtualHost<?> virtualHost = _broker.findVirtualHostByName(virtualHostName); if (virtualHost == null) { - throw new IllegalArgumentException("Virtual host with name '" + vhost + "' is not found."); + throw new IllegalArgumentException("Virtual host with name '" + virtualHostName + "' is not found."); } - security = virtualHost.getSecurityManager(); } methodName = getMethodName(method, args); Operation operation = (isAccessMethod(methodName) || impact == MBeanOperationInfo.INFO) ? Operation.ACCESS : Operation.UPDATE; - security.authoriseMethod(operation, type, methodName); + + SecurityManager security = _broker.getSecurityManager(); + security.authoriseMethod(operation, type, methodName, virtualHostName); if (_managementRightsInferAllAccess) { diff --git a/qpid/java/build.deps b/qpid/java/build.deps index f4496ce8ab..a0efbea658 100644 --- a/qpid/java/build.deps +++ b/qpid/java/build.deps @@ -57,6 +57,8 @@ jetty-servlet=lib/required/jetty-servlet-8.1.14.v20131031.jar jetty-websocket=lib/required/jetty-websocket-8.1.14.v20131031.jar servlet-api=${geronimo-servlet} +cryptojs=lib/required/cryptojs-3.1.2.jar + dojo-version=1.9.1 dojo=lib/required/dojo-${dojo-version}.zip @@ -80,7 +82,7 @@ broker-core.libs=${commons-cli} ${commons-logging} ${log4j} ${slf4j-log4j} \ #Borrow the broker-core libs, hack for release binary generation broker.libs=${broker-core.libs} -broker-plugins-management-http.libs=${dojo} +broker-plugins-management-http.libs=${dojo} ${cryptojs} broker-plugins.libs=${log4j} ${commons.libs} test.libs=${slf4j-log4j} ${log4j} ${junit} ${slf4j-api} ${mockito-all} diff --git a/qpid/java/client/src/main/java/client.bnd b/qpid/java/client/src/main/java/client.bnd index 63b1e3b34c..c2abcd55b5 100755 --- a/qpid/java/client/src/main/java/client.bnd +++ b/qpid/java/client/src/main/java/client.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.27.0 +ver: 0.29.0 Bundle-SymbolicName: qpid-client Bundle-Version: ${ver} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties b/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties index 8855a040ea..109ff9942a 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.properties @@ -31,3 +31,5 @@ CRAM-MD5.4=org.apache.qpid.client.security.UsernamePasswordCallbackHandler AMQPLAIN.5=org.apache.qpid.client.security.UsernamePasswordCallbackHandler PLAIN.6=org.apache.qpid.client.security.UsernamePasswordCallbackHandler ANONYMOUS.7=org.apache.qpid.client.security.UsernamePasswordCallbackHandler +SCRAM-SHA1.8=org.apache.qpid.client.security.UsernamePasswordCallbackHandler + diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties index b903208927..c2cd671bdf 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties @@ -19,3 +19,4 @@ AMQPLAIN=org.apache.qpid.client.security.amqplain.AmqPlainSaslClientFactory CRAM-MD5-HASHED=org.apache.qpid.client.security.crammd5hashed.CRAMMD5HashedSaslClientFactory ANONYMOUS=org.apache.qpid.client.security.anonymous.AnonymousSaslClientFactory +SCRAM-SHA1=org.apache.qpid.client.security.scram.ScramSHA1SaslClientFactory diff --git a/qpid/java/common.xml b/qpid/java/common.xml index 889cf3c4d8..87b8f2d175 100644 --- a/qpid/java/common.xml +++ b/qpid/java/common.xml @@ -24,9 +24,9 @@ <property name="project.name" value="qpid"/> <!-- Version used for standard build output --> - <property name="project.version" value="0.27"/> + <property name="project.version" value="0.29"/> <!-- The release version used for maven output. SNAPSHOT added via maven.version.suffix --> - <property name="project.version.maven" value="0.28"/> + <property name="project.version.maven" value="0.30"/> <property name="project.url" value="http://qpid.apache.org"/> <property name="project.groupid" value="org.apache.qpid"/> <property name="project.namever" value="${project.name}-${project.version}"/> diff --git a/qpid/java/common/src/main/java/common.bnd b/qpid/java/common/src/main/java/common.bnd index 76ee9d740f..498562f3c1 100755 --- a/qpid/java/common/src/main/java/common.bnd +++ b/qpid/java/common/src/main/java/common.bnd @@ -17,7 +17,7 @@ # under the License.
#
-ver: 0.27.0
+ver: 0.29.0
Bundle-SymbolicName: qpid-common
Bundle-Version: ${ver}
diff --git a/qpid/java/ivy.retrieve.xml b/qpid/java/ivy.retrieve.xml index dcb97fcf9f..e604023940 100644 --- a/qpid/java/ivy.retrieve.xml +++ b/qpid/java/ivy.retrieve.xml @@ -70,6 +70,7 @@ <dependency org="xalan" name="xalan" rev="2.7.0" transitive="false"/> <dependency org="velocity" name="velocity" rev="1.4" transitive="false"/> <dependency org="velocity" name="velocity-dep" rev="1.4" transitive="false"/> + <dependency org="org.webjars" name="cryptojs" rev="3.1.2" transitive="false"/> <dependency org="org.dojotoolkit" name="dojo" rev="1.9.1" transitive="false"> <artifact name="dojo" type="zip"/> </dependency> diff --git a/qpid/java/management/common/src/main/java/management-common.bnd b/qpid/java/management/common/src/main/java/management-common.bnd index 459c3e60a8..cb080c9680 100644 --- a/qpid/java/management/common/src/main/java/management-common.bnd +++ b/qpid/java/management/common/src/main/java/management-common.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.27.0 +ver: 0.29.0 Bundle-SymbolicName: qpid-management-common Bundle-Version: ${ver} diff --git a/qpid/java/maven/qpid-enforcer-plugin-rules/pom.xml b/qpid/java/maven/qpid-enforcer-plugin-rules/pom.xml new file mode 100644 index 0000000000..47a393b888 --- /dev/null +++ b/qpid/java/maven/qpid-enforcer-plugin-rules/pom.xml @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.apache.qpid.enforcer</groupId> + <artifactId>qpid-enforcer-plugin-rules</artifactId> + <version>1.0-SNAPSHOT</version> + <name>Qpid Maven Enforcer Plugin Rules</name> + <description>Custom maven enforcer plugin rules for the Qpid maven build.</description> + + <properties> + <api.version>1.3.1</api.version> + <maven.version>2.2.1</maven.version> + <plexus.container.version>1.5.5</plexus.container.version> + <junit.version>4.11</junit.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.maven.enforcer</groupId> + <artifactId>enforcer-rules</artifactId> + <version>${api.version}</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-io</artifactId> + <version>1.3.2</version> + </dependency> + <dependency> + <groupId>org.apache.maven.enforcer</groupId> + <artifactId>enforcer-api</artifactId> + <version>${api.version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-project</artifactId> + <version>${maven.version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-core</artifactId> + <version>${maven.version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-artifact</artifactId> + <version>${maven.version}</version> + </dependency> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-plugin-api</artifactId> + <version>${maven.version}</version> + </dependency> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-container-default</artifactId> + <version>${plexus.container.version}</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.maven.enforcer</groupId> + <artifactId>enforcer-rules</artifactId> + <version>${api.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + </build> + +</project> diff --git a/qpid/java/maven/qpid-enforcer-plugin-rules/src/main/java/org/apache/qpid/maven/enforcer/rule/RequireFileContentsAreEquivalent.java b/qpid/java/maven/qpid-enforcer-plugin-rules/src/main/java/org/apache/qpid/maven/enforcer/rule/RequireFileContentsAreEquivalent.java new file mode 100644 index 0000000000..11186a5c7f --- /dev/null +++ b/qpid/java/maven/qpid-enforcer-plugin-rules/src/main/java/org/apache/qpid/maven/enforcer/rule/RequireFileContentsAreEquivalent.java @@ -0,0 +1,104 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package org.apache.qpid.maven.enforcer.rule; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.enforcer.rule.api.EnforcerRule; +import org.apache.maven.enforcer.rule.api.EnforcerRuleException; +import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; +import org.apache.maven.plugins.enforcer.AbstractStandardEnforcerRule; + +public class RequireFileContentsAreEquivalent extends AbstractStandardEnforcerRule +{ + + final static String WHITESPACE_REGEX = "\\s+"; + final static String EMPTY_STRING = ""; + + File[] files; + + @Override + public void execute(final EnforcerRuleHelper enforcerRuleHelper) throws EnforcerRuleException + { + if (files.length < 2) + { + throw new EnforcerRuleException("The file list must contain at least two files for comparison."); + } + + boolean success = true; + boolean firstTime = true; + String referenceContent = null; + + for (final File file : files) + { + try + { + final String fileContent = FileUtils.readFileToString(file); + if (firstTime) + { + referenceContent = fileContent; + firstTime = false; + } + else if (referenceContent != null && fileContent != null) + { + final String strippedReferenceContent = referenceContent.replaceAll(WHITESPACE_REGEX, EMPTY_STRING); + final String strippedFileContent = fileContent.replaceAll(WHITESPACE_REGEX, EMPTY_STRING); + if (!strippedReferenceContent.equalsIgnoreCase(strippedFileContent)) + { + success = false; + break; + } + } + else + { + throw new EnforcerRuleException("Unable to read file contents"); + } + } + catch (final IOException ioe) + { + throw new EnforcerRuleException("Cannot process file : " + file.getName(), ioe); + } + } + + if (!success) + { + throw new EnforcerRuleException("Files specified are not equal in content"); + } + } + + @Override + public String getCacheId() + { + return Integer.toString(Arrays.hashCode(files)); + } + + @Override + public boolean isCacheable() + { + return true; + } + + @Override + public boolean isResultValid(EnforcerRule arg0) + { + return true; + } + +} diff --git a/qpid/java/maven/qpid-enforcer-plugin-rules/src/test/java/org/apache/qpid/maven/enforcer/rule/TestRequireFileContentsAreEquivalent.java b/qpid/java/maven/qpid-enforcer-plugin-rules/src/test/java/org/apache/qpid/maven/enforcer/rule/TestRequireFileContentsAreEquivalent.java new file mode 100644 index 0000000000..7a5d214972 --- /dev/null +++ b/qpid/java/maven/qpid-enforcer-plugin-rules/src/test/java/org/apache/qpid/maven/enforcer/rule/TestRequireFileContentsAreEquivalent.java @@ -0,0 +1,149 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package org.apache.qpid.maven.enforcer.rule; + +import java.io.File; +import java.io.IOException; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.enforcer.rule.api.EnforcerRuleException; +import org.apache.maven.plugins.enforcer.EnforcerTestUtils; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class TestRequireFileContentsAreEquivalent +{ + final RequireFileContentsAreEquivalent rule = new RequireFileContentsAreEquivalent(); + + final static String TEST_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; + final static String TEST_TEXT_WHITESPACE = "Lorem ipsum dolor sit amet,\n consectetur adipiscing \t elit. "; + final static String ALTERNATE_TEST_TEXT_WHITESPACE = " Lorem \t ip sum dolor \n sit amet,\n consectetur adipiscing \t elit."; + final static String DIFFERENT_TEST_TEXT = "Donec velit felis, semper dapibus mattis vitae"; + + @Test + public void testDifferentContentFiles() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, DIFFERENT_TEST_TEXT); + + rule.files = new File[] + { f1, f2 }; + + try + { + rule.execute(EnforcerTestUtils.getHelper()); + Assert.fail("Files with different content should have failed enforcer rule"); + } + catch (final EnforcerRuleException ere) + { + // do nothing + } + } + + @Test + public void testIdenticalContentFiles() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, TEST_TEXT); + + rule.files = new File[] + { f1, f2 }; + + rule.execute(EnforcerTestUtils.getHelper()); + } + + @Test + public void testUsingOneFileTwice() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + + rule.files = new File[] + { f1, f1 }; + + rule.execute(EnforcerTestUtils.getHelper()); + } + + @Test + public void testSimilarFiles() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, TEST_TEXT_WHITESPACE); + + rule.files = new File[] + { f1, f2 }; + + rule.execute(EnforcerTestUtils.getHelper()); + } + + @Test + public void testMultipleFilesOneDifferent() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, TEST_TEXT_WHITESPACE); + final File f3 = createTestFile(3, ALTERNATE_TEST_TEXT_WHITESPACE); + final File f4 = createTestFile(4, DIFFERENT_TEST_TEXT); + + rule.files = new File[] + { f1, f2, f3, f4 }; + + try + { + rule.execute(EnforcerTestUtils.getHelper()); + Assert.fail("Files with different content should have failed enforcer rule"); + } + catch (final EnforcerRuleException ere) + { + // do nothing + } + } + + @Test + public void testMultipleFilesAllSimilar() throws Exception + { + final File f1 = createTestFile(1, TEST_TEXT); + final File f2 = createTestFile(2, TEST_TEXT); + final File f3 = createTestFile(3, TEST_TEXT_WHITESPACE); + final File f4 = createTestFile(4, ALTERNATE_TEST_TEXT_WHITESPACE); + + rule.files = new File[] + { f1, f2, f3, f4 }; + + rule.execute(EnforcerTestUtils.getHelper()); + } + + @After + public void deleteTestFiles() throws Exception + { + for (File file : rule.files) + { + if (file.exists()) + { + file.delete(); + } + } + } + + private File createTestFile(final int id, final String content) throws IOException + { + final File file = File.createTempFile(TestRequireFileContentsAreEquivalent.class.getName() + + "-testfile" + id, "tmp"); + file.deleteOnExit(); + FileUtils.writeStringToFile(file, content); + return file; + } +} diff --git a/qpid/java/pom.xml b/qpid/java/pom.xml index f5fe52e5e1..7c99ea6c85 100644 --- a/qpid/java/pom.xml +++ b/qpid/java/pom.xml @@ -54,7 +54,7 @@ <notice.text>Apache Qpid${line.separator}Copyright ${project.inceptionYear}-2014${line.separator}Apache Software Foundation${line.separator}This product includes software developed at Apache Software Foundation (http://www.apache.org/)</notice.text> <!-- enforcer plugin config properties --> - <supported-test-profiles-regex>(java-mms.0-9|java-mms.0-9-1|java-mms.0-10|java-bdb.0-9|java-bdb.0-9-1|java-bdb.0-10|java-dby-mem.0-9|java-dby-mem.0-9-1|java-dby-mem.0-10)</supported-test-profiles-regex> + <supported-test-profiles-regex>(java-mms.0-9|java-mms.0-9-1|java-mms.0-10|java-bdb.0-9|java-bdb.0-9-1|java-bdb.0-10|java-dby.0-9|java-dby.0-9-1|java-dby.0-10|java-dby-mem.0-9|java-dby-mem.0-9-1|java-dby-mem.0-10)</supported-test-profiles-regex> <!-- plugin properties--> <license-maven-plugin-output-dir>${project.build.directory}/generated-licenses</license-maven-plugin-output-dir> @@ -64,12 +64,21 @@ <qpid.home>${basedir}</qpid.home> <!-- override for broker tests --> <qpid.home.qbtc.output>${qpid.home}${file.separator}target${file.separator}qbtc-output</qpid.home.qbtc.output> <!-- override for broker tests --> <qpid.work>${project.build.directory}${file.separator}QPID_WORK</qpid.work> + <profile>java-mms.0-10</profile> - <profile.excludes>JavaTransientExcludes Java010Excludes</profile.excludes> + <profile.broker.language>java</profile.broker.language> + <profile.broker.type>internal</profile.broker.type> + <profile.broker.stopped>Exception</profile.broker.stopped> + <profile.broker.ready>BRK-1004</profile.broker.ready> + <profile.broker.command>${qpid.home}${file.separator}bin${file.separator}qpid-server -sp @STORE_PATH -st @STORE_TYPE -l @LOG_CONFIG_FILE</profile.broker.command> + <profile.broker.command.windows>${qpid.home}${file.separator}bin${file.separator}qpid-server.bat -sp @STORE_PATH -st @STORE_TYPE -l @LOG_CONFIG_FILE</profile.broker.command.windows> + <profile.test.excludes>Excludes JavaExcludes ${profile}.excludes ${profile.specific.excludes}</profile.test.excludes> + <profile.specific.excludes>JavaTransientExcludes Java010Excludes</profile.specific.excludes> <profile.broker.version>v0_10</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>false</profile.broker.persistent> <profile.messagestore.type>Memory</profile.messagestore.type> + <profile.broker.clean.between.tests>true</profile.broker.clean.between.tests> </properties> <modules> @@ -146,7 +155,11 @@ <tasks> <echo>Qpid Test Profile Properties</echo> <echo>[profile] ${profile}</echo> - <echo>[profile.excludes] ${profile.excludes}</echo> + <echo>[profile.broker.language] ${profile.broker.language}</echo> + <echo>[profile.broker.type] ${profile.broker.type}</echo> + <echo>[profile.broker.command] ${profile.broker.command}</echo> + <echo>[profile.specific.excludes] ${profile.specific.excludes}</echo> + <echo>[profile.test.excludes] ${profile.test.excludes}</echo> <echo>[profile.broker.version] ${profile.broker.version}</echo> <echo>[profile.qpid.broker_default_amqp_protocol_excludes] ${profile.qpid.broker_default_amqp_protocol_excludes}</echo> <echo>[profile.broker.persistent] ${profile.broker.persistent}</echo> @@ -216,20 +229,21 @@ <test.exclude>true</test.exclude> <test.mem>512M</test.mem> <profile.clustered>false</profile.clustered> - <broker.language>java</broker.language> - <broker.type>internal</broker.type> - <broker.stopped>Exception</broker.stopped> - <broker.ready>BRK-1004</broker.ready> - <broker.command>${qpid.home}${file.separator}bin${file.separator}qpid-server -sp @STORE_PATH -st @STORE_TYPE -l @LOG_CONFIG_FILE</broker.command> - <broker.command.windows>${qpid.home}${file.separator}bin${file.separator}qpid-server.bat -sp @STORE_PATH -st @STORE_TYPE -l @LOG_CONFIG_FILE</broker.command.windows> <!-- Profile Based Values --> - <test.excludes>Excludes JavaExcludes ${profile}.excludes ${profile.excludes}</test.excludes> + <profile>${profile}</profile> + <broker.language>${profile.broker.language}</broker.language> + <broker.type>${profile.broker.type}</broker.type> + <broker.stopped>${profile.broker.stopped}</broker.stopped> + <broker.ready>${profile.broker.ready}</broker.ready> + <broker.command>${profile.broker.command}</broker.command> + <broker.command.windows>${profile.broker.command.windows}</broker.command.windows> + <test.excludes>${profile.test.excludes}</test.excludes> <broker.version>${profile.broker.version}</broker.version> <qpid.broker_default_amqp_protocol_excludes>${profile.qpid.broker_default_amqp_protocol_excludes}</qpid.broker_default_amqp_protocol_excludes> <broker.persistent>${profile.broker.persistent}</broker.persistent> <messagestore.type>${profile.messagestore.type}</messagestore.type> - <profile>${profile}</profile> + <broker.clean.between.tests>${profile.broker.clean.between.tests}</broker.clean.between.tests> <!-- This must be a child of qpid home currently due to the horrible mechanics of QBTC --> <test.output>${qpid.home.qbtc.output}</test.output> @@ -417,7 +431,7 @@ </activation> <properties> <profile>java-mms.0-10</profile> - <profile.excludes>JavaTransientExcludes Java010Excludes</profile.excludes> + <profile.specific.excludes>JavaTransientExcludes Java010Excludes</profile.specific.excludes> <profile.broker.version>v0_10</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>false</profile.broker.persistent> @@ -435,7 +449,7 @@ </activation> <properties> <profile>java-mms.0-9-1</profile> - <profile.excludes>JavaTransientExcludes XAExcludes JavaPre010Excludes</profile.excludes> + <profile.specific.excludes>JavaTransientExcludes XAExcludes JavaPre010Excludes</profile.specific.excludes> <profile.broker.version>v0_9_1</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0,AMQP_0_10</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>false</profile.broker.persistent> @@ -453,7 +467,7 @@ </activation> <properties> <profile>java-mms.0-9</profile> - <profile.excludes>JavaTransientExcludes XAExcludes JavaPre010Excludes</profile.excludes> + <profile.specific.excludes>JavaTransientExcludes XAExcludes JavaPre010Excludes</profile.specific.excludes> <profile.broker.version>v0_9</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0,AMQP_0_10,AMQP_0_9_1</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>false</profile.broker.persistent> @@ -471,7 +485,7 @@ </activation> <properties> <profile>java-bdb.0-10</profile> - <profile.excludes>JavaPersistentExcludes Java010Excludes JavaBDBExcludes</profile.excludes> + <profile.specific.excludes>JavaPersistentExcludes Java010Excludes JavaBDBExcludes</profile.specific.excludes> <profile.broker.version>v0_10</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>true</profile.broker.persistent> @@ -489,7 +503,7 @@ </activation> <properties> <profile>java-bdb.0-9-1</profile> - <profile.excludes>JavaPersistentExcludes XAExcludes JavaPre010Excludes JavaBDBExcludes</profile.excludes> + <profile.specific.excludes>JavaPersistentExcludes XAExcludes JavaPre010Excludes JavaBDBExcludes</profile.specific.excludes> <profile.broker.version>v0_9_1</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0,AMQP_0_10</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>true</profile.broker.persistent> @@ -507,7 +521,7 @@ </activation> <properties> <profile>java-bdb.0-9</profile> - <profile.excludes>JavaPersistentExcludes XAExcludes JavaPre010Excludes JavaBDBExcludes</profile.excludes> + <profile.specific.excludes>JavaPersistentExcludes XAExcludes JavaPre010Excludes JavaBDBExcludes</profile.specific.excludes> <profile.broker.version>v0_9</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0,AMQP_0_10,AMQP_0_9_1</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>true</profile.broker.persistent> @@ -525,7 +539,7 @@ </activation> <properties> <profile>java-dby-mem.0-10</profile> - <profile.excludes>JavaPersistentExcludes JavaDerbyExcludes Java010Excludes</profile.excludes> + <profile.specific.excludes>JavaPersistentExcludes JavaDerbyExcludes Java010Excludes</profile.specific.excludes> <profile.broker.version>v0_10</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>true</profile.broker.persistent> @@ -543,7 +557,7 @@ </activation> <properties> <profile>java-dby-mem.0-9-1</profile> - <profile.excludes>JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes</profile.excludes> + <profile.specific.excludes>JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes</profile.specific.excludes> <profile.broker.version>v0_9_1</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0,AMQP_0_10</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>true</profile.broker.persistent> @@ -561,7 +575,7 @@ </activation> <properties> <profile>java-dby-mem.0-9</profile> - <profile.excludes>JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes</profile.excludes> + <profile.specific.excludes>JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes</profile.specific.excludes> <profile.broker.version>v0_9</profile.broker.version> <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0,AMQP_0_10,AMQP_0_9_1</profile.qpid.broker_default_amqp_protocol_excludes> <profile.broker.persistent>true</profile.broker.persistent> @@ -569,6 +583,63 @@ </properties> </profile> + <profile> + <id>java-dby.0-10</id> + <activation> + <property> + <name>profile</name> + <value>java-dby.0-10</value> + </property> + </activation> + <properties> + <profile>java-dby.0-10</profile> + <profile.specific.excludes>JavaPersistentExcludes JavaDerbyExcludes Java010Excludes</profile.specific.excludes> + <profile.broker.version>v0_10</profile.broker.version> + <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0</profile.qpid.broker_default_amqp_protocol_excludes> + <profile.broker.virtualhosts-config>${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby.xml</profile.broker.virtualhosts-config> + <profile.broker.persistent>true</profile.broker.persistent> + <profile.messagestore.class.name>org.apache.qpid.server.store.derby.DerbyMessageStore</profile.messagestore.class.name> + </properties> + </profile> + + <profile> + <id>java-dby.0-9-1</id> + <activation> + <property> + <name>profile</name> + <value>java-dby.0-9-1</value> + </property> + </activation> + <properties> + <profile>java-dby.0-9-1</profile> + <profile.specific.excludes>JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes</profile.specific.excludes> + <profile.broker.version>v0_9_1</profile.broker.version> + <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0,AMQP_0_10</profile.qpid.broker_default_amqp_protocol_excludes> + <profile.broker.virtualhosts-config>${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby.xml</profile.broker.virtualhosts-config> + <profile.broker.persistent>true</profile.broker.persistent> + <profile.messagestore.class.name>org.apache.qpid.server.store.derby.DerbyMessageStore</profile.messagestore.class.name> + </properties> + </profile> + + <profile> + <id>java-dby.0-9</id> + <activation> + <property> + <name>profile</name> + <value>java-dby.0-9</value> + </property> + </activation> + <properties> + <profile>java-dby.0-9</profile> + <profile.specific.excludes>JavaPersistentExcludes JavaDerbyExcludes XAExcludes JavaPre010Excludes</profile.specific.excludes> + <profile.broker.version>v0_9</profile.broker.version> + <profile.qpid.broker_default_amqp_protocol_excludes>AMQP_1_0,AMQP_0_10,AMQP_0_9_1</profile.qpid.broker_default_amqp_protocol_excludes> + <profile.broker.virtualhosts-config>${QPID_HOME}${file.separator}etc${file.separator}virtualhosts-systests-derby.xml</profile.broker.virtualhosts-config> + <profile.broker.persistent>true</profile.broker.persistent> + <profile.messagestore.class.name>org.apache.qpid.server.store.derby.DerbyMessageStore</profile.messagestore.class.name> + </properties> + </profile> + </profiles> </project> diff --git a/qpid/java/qpid-perftests-systests/pom.xml b/qpid/java/qpid-perftests-systests/pom.xml index 6856fd9d17..4557e903ff 100644 --- a/qpid/java/qpid-perftests-systests/pom.xml +++ b/qpid/java/qpid-perftests-systests/pom.xml @@ -86,6 +86,20 @@ <scope>runtime</scope> </dependency> + <dependency> + <groupId>org.apache.qpid</groupId> + <artifactId>qpid-bdbstore</artifactId> + <version>${project.version}</version> + <scope>test</scope> + <optional>true</optional> + </dependency> + + <dependency> + <groupId>com.sleepycat</groupId> + <artifactId>je</artifactId> + <scope>test</scope> + <optional>true</optional> + </dependency> </dependencies> <build> diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java index 37f960a65a..a0188626ee 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java @@ -49,8 +49,7 @@ public class AccessControlLoggingTest extends AbstractTestLogging public void setUp() throws Exception { // Write out ACL for this test - AbstractACLTestCase.writeACLFileUtil(this, "test", - "ACL ALLOW client ACCESS VIRTUALHOST", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW client ACCESS VIRTUALHOST", "ACL ALLOW client CREATE QUEUE name='allow'", "ACL ALLOW-LOG client CREATE QUEUE name='allow-log'", "ACL DENY client CREATE QUEUE name='deny'", diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java index 72626e5089..789ad420d8 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java @@ -24,9 +24,7 @@ import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQConnectionURL; import org.apache.qpid.jms.ConnectionListener; import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.test.utils.QpidBrokerTestCase; -import org.apache.qpid.test.utils.TestBrokerConfiguration; import org.apache.qpid.url.URLSyntaxException; import javax.jms.Connection; @@ -45,12 +43,10 @@ import java.util.concurrent.TimeUnit; /** * Abstract test case for ACLs. - * + * * This base class contains convenience methods to manage ACL files and implements a mechanism that allows each * test method to run its own setup code before the broker starts. - * - * TODO move the pre broker-startup setup method invocation code to {@link QpidBrokerTestCase} - * + * * @see ExternalACLTest * @see ExternalACLJMXTest * @see ExhaustiveACLTest @@ -80,7 +76,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements { throw (Exception) e.getTargetException(); } - + super.setUp(); } @@ -97,26 +93,18 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements //that we provoked with authentication failures, where the test passes - we can ignore on con close } } - - public void writeACLFile(final String vhost, final String...rules) throws IOException + + public void writeACLFile(final String...rules) throws IOException { - writeACLFileUtil(this, vhost, rules); + writeACLFileUtil(this, rules); } - public static void writeACLFileUtil(QpidBrokerTestCase testcase, String vhost, String...rules) throws IOException + public static void writeACLFileUtil(QpidBrokerTestCase testcase, String...rules) throws IOException { File aclFile = File.createTempFile(testcase.getClass().getSimpleName(), testcase.getName()); aclFile.deleteOnExit(); - TestBrokerConfiguration config = testcase.getBrokerConfiguration(); - if (vhost == null) - { - config.addAclFileConfiguration(aclFile.getAbsolutePath()); - } - else - { - config.setObjectAttribute(vhost, VirtualHost.SECURITY_ACL, aclFile.getAbsolutePath()); - } + testcase.getBrokerConfiguration().addAclFileConfiguration(aclFile.getAbsolutePath()); PrintWriter out = new PrintWriter(new FileWriter(aclFile)); out.println(String.format("# %s", testcase.getName())); @@ -128,7 +116,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements } /** - * Creates a connection to the broker, and sets a connection listener to prevent failover and an exception listener + * Creates a connection to the broker, and sets a connection listener to prevent failover and an exception listener * with a {@link CountDownLatch} to synchronise in the {@link #check403Exception(Throwable)} method and allow the * {@link #tearDown()} method to complete properly. */ @@ -138,8 +126,8 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements //Prevent Failover connection.setConnectionListener(this); - - //QPID-2081: use a latch to sync on exception causing connection close, to work + + //QPID-2081: use a latch to sync on exception causing connection close, to work //around the connection close race during tearDown() causing sporadic failures _exceptionReceived = new CountDownLatch(1); @@ -196,8 +184,8 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements assertNotNull("There was no linked exception", t); assertTrue("Wrong linked exception type : " + t.getClass(), t instanceof AMQException); assertEquals("Incorrect error code received", 403, ((AMQException) t).getErrorCode().getCode()); - - //use the latch to ensure the control thread waits long enough for the exception thread + + //use the latch to ensure the control thread waits long enough for the exception thread //to have done enough to mark the connection closed before teardown commences assertTrue("Timed out waiting for conneciton to report close", _exceptionReceived.await(2, TimeUnit.SECONDS)); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java index 50c80692dd..07fe88b38f 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExhaustiveACLTest.java @@ -39,20 +39,20 @@ public class ExhaustiveACLTest extends AbstractACLTestCase /** * Creates a queue. - * + * * Connects to the broker as a particular user and create the named queue on a virtual host, with the provided * parameters. Uses a new {@link Connection} and {@link Session} and closes them afterwards. */ private void createQueue(String vhost, String user, String name, boolean autoDelete, boolean durable) throws Exception { - Connection conn = getConnection(vhost, user, "guest"); + Connection conn = getConnection(vhost, user, "guest"); Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); conn.start(); ((AMQSession<?, ?>) sess).createQueue(new AMQShortString(name), autoDelete, durable, false); sess.commit(); conn.close(); } - + /** * Calls {@link #createQueue(String, String, String, boolean, boolean)} with the provided parameters and checks that * no exceptions were thrown. @@ -61,7 +61,7 @@ public class ExhaustiveACLTest extends AbstractACLTestCase { try { - createQueue(vhost, user, name, autoDelete, durable); + createQueue(vhost, user, name, autoDelete, durable); } catch (AMQException e) { @@ -72,7 +72,7 @@ public class ExhaustiveACLTest extends AbstractACLTestCase /** * Calls {@link #createQueue(String, String, String, boolean, boolean)} with the provided parameters and checks that - * the exception thrown was an {@link AMQConstant#ACCESS_REFUSED} or 403 error code. + * the exception thrown was an {@link AMQConstant#ACCESS_REFUSED} or 403 error code. */ private void createQueueFailure(String vhost, String user, String name, boolean autoDelete, boolean durable) throws Exception { @@ -87,67 +87,64 @@ public class ExhaustiveACLTest extends AbstractACLTestCase assertEquals("Should be an ACCESS_REFUSED error", 403, e.getErrorCode().getCode()); } } - + public void setUpAuthoriseCreateQueueAutodelete() throws Exception { - writeACLFile("test", - "acl allow client access virtualhost", + writeACLFile("acl allow client access virtualhost", "acl allow server access virtualhost", "acl allow client create queue name=\"temp.true.*\" autodelete=true", "acl allow client create queue name=\"temp.false.*\" autodelete=false", - "acl deny client create queue", - "acl allow client delete queue", + "acl deny client create queue", + "acl allow client delete queue", "acl deny all create queue" ); } - + /** * Test creation of temporary queues, with the autodelete property set to true. */ public void testAuthoriseCreateQueueAutodelete() throws Exception { - createQueueSuccess("test", "client", "temp.true.00", true, false); + createQueueSuccess("test", "client", "temp.true.00", true, false); createQueueSuccess("test", "client", "temp.true.01", true, false); createQueueSuccess("test", "client", "temp.true.02", true, true); - createQueueSuccess("test", "client", "temp.false.03", false, false); + createQueueSuccess("test", "client", "temp.false.03", false, false); createQueueSuccess("test", "client", "temp.false.04", false, false); createQueueSuccess("test", "client", "temp.false.05", false, true); - createQueueFailure("test", "client", "temp.true.06", false, false); + createQueueFailure("test", "client", "temp.true.06", false, false); createQueueFailure("test", "client", "temp.false.07", true, false); - createQueueFailure("test", "server", "temp.true.08", true, false); + createQueueFailure("test", "server", "temp.true.08", true, false); createQueueFailure("test", "client", "temp.other.09", false, false); } - + public void setUpAuthoriseCreateQueue() throws Exception { - writeACLFile("test", - "acl allow client access virtualhost", + writeACLFile("acl allow client access virtualhost", "acl allow server access virtualhost", "acl allow client create queue name=\"create.*\"" ); } - + /** * Tests creation of named queues. * - * If a named queue is specified + * If a named queue is specified */ public void testAuthoriseCreateQueue() throws Exception { createQueueSuccess("test", "client", "create.00", true, true); createQueueSuccess("test", "client", "create.01", true, false); createQueueSuccess("test", "client", "create.02", false, true); - createQueueSuccess("test", "client", "create.03", true, false); + createQueueSuccess("test", "client", "create.03", true, false); createQueueFailure("test", "server", "create.04", true, true); createQueueFailure("test", "server", "create.05", true, false); createQueueFailure("test", "server", "create.06", false, true); - createQueueFailure("test", "server", "create.07", true, false); + createQueueFailure("test", "server", "create.07", true, false); } - + public void setUpAuthoriseCreateQueueBoth() throws Exception { - writeACLFile("test", - "acl allow all access virtualhost", + writeACLFile("acl allow all access virtualhost", "acl allow client create queue name=\"create.*\"", "acl allow all create queue temporary=true" ); @@ -156,16 +153,16 @@ public class ExhaustiveACLTest extends AbstractACLTestCase /** * Tests creation of named queues. * - * If a named queue is specified + * If a named queue is specified */ public void testAuthoriseCreateQueueBoth() throws Exception { createQueueSuccess("test", "client", "create.00", true, false); createQueueSuccess("test", "client", "create.01", false, false); createQueueFailure("test", "server", "create.02", false, false); - createQueueFailure("test", "guest", "create.03", false, false); + createQueueFailure("test", "guest", "create.03", false, false); createQueueSuccess("test", "client", "tmp.00", true, false); - createQueueSuccess("test", "server", "tmp.01", true, false); + createQueueSuccess("test", "server", "tmp.01", true, false); createQueueSuccess("test", "guest", "tmp.02", true, false); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java index 389ac5ff4d..7a1a6cd639 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java @@ -56,7 +56,7 @@ public class ExternalACLJMXTest extends AbstractACLTestCase super.setUp(); _jmx.open(); } - + @Override public void tearDown() throws Exception { @@ -66,9 +66,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase public void setUpDenyAllIsCatchAllRule() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "#No more rules, default catch all (deny all) should apply"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "#No more rules, default catch all (deny all) should apply"); } public void testDenyAllIsCatchAllRule() throws Exception @@ -107,7 +106,7 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpAllowAll() throws Exception { - writeACLFile(null, "ACL ALLOW ALL ALL"); + writeACLFile("ACL ALLOW ALL ALL"); } public void testAllowAll() throws Exception @@ -118,24 +117,17 @@ public class ExternalACLJMXTest extends AbstractACLTestCase // PASS } - /** - * admin user is denied at broker level but allowed at vhost level. - */ - public void setUpVhostAllowOverridesGlobalDeny() throws Exception + public void setUpVhostWithName() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); - writeACLFile(TEST_VHOST, - "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue' virtualhost_name='"+ TEST_VHOST + "'", + "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue' virtualhost_name='"+ TEST2_VHOST + "'"); } - public void testVhostAllowOverridesGlobalDeny() throws Exception + public void testVhostWithName() throws Exception { - //try a vhost-level method on the allowed vhost _jmx.createQueue(TEST_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); - //try a vhost-level method on a different vhost try { _jmx.createQueue(TEST2_VHOST, getTestQueueName(), TEST_QUEUE_OWNER, true); @@ -153,9 +145,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpUpdateComponentOnlyAllow() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager'"); } public void testUpdateComponentOnlyAllow() throws Exception @@ -172,9 +163,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpUpdateMethodOnlyAllow() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW admin UPDATE METHOD"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD"); } public void testUpdateMethodOnlyAllow() throws Exception @@ -187,12 +177,12 @@ public class ExternalACLJMXTest extends AbstractACLTestCase /** - * admin user has JMX right, AMPQ right is irrelevant. + * admin user has JMX right, AMQP right is irrelevant. */ public void setUpCreateQueueSuccess() throws Exception { - writeACLFile(null, "ACL ALLOW admin ACCESS MANAGEMENT"); - writeACLFile(TEST_VHOST, "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); } public void testCreateQueueSuccess() throws Exception @@ -202,14 +192,13 @@ public class ExternalACLJMXTest extends AbstractACLTestCase /** - * admin user has JMX right, verifies lack of AMPQ rights is irrelevant. + * admin user has JMX right, verifies lack of AMQP rights is irrelevant. */ public void setUpCreateQueueSuccessNoAMQPRights() throws Exception { - writeACLFile(null, "ACL ALLOW admin ACCESS MANAGEMENT"); - writeACLFile(TEST_VHOST, - "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'", - "ACL DENY admin CREATE QUEUE"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'", + "ACL DENY admin CREATE QUEUE"); } public void testCreateQueueSuccessNoAMQPRights() throws Exception @@ -219,13 +208,12 @@ public class ExternalACLJMXTest extends AbstractACLTestCase /** - * admin user does not have JMX right, AMPQ right is irrelevant. + * admin user does not have JMX right, AMQP right is irrelevant. */ public void setUpCreateQueueDenied() throws Exception { - writeACLFile(null, "ACL ALLOW admin ACCESS MANAGEMENT"); - writeACLFile(TEST_VHOST, - "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL DENY admin UPDATE METHOD component='VirtualHost.VirtualHostManager' name='createNewQueue'"); } public void testCreateQueueDenied() throws Exception @@ -247,9 +235,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpServerInformationUpdateDenied() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL DENY admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL DENY admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); } public void testServerInformationUpdateDenied() throws Exception @@ -272,9 +259,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpServerInformationAccessGranted() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW-LOG admin ACCESS METHOD component='ServerInformation' name='getManagementApiMajorVersion'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW-LOG admin ACCESS METHOD component='ServerInformation' name='getManagementApiMajorVersion'"); } public void testServerInformationAccessGranted() throws Exception @@ -299,9 +285,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpServerInformationUpdateMethodPermission() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin UPDATE METHOD component='ServerInformation' name='resetStatistics'"); } public void testServerInformationUpdateMethodPermission() throws Exception @@ -317,9 +302,8 @@ public class ExternalACLJMXTest extends AbstractACLTestCase */ public void setUpServerInformationAllMethodPermissions() throws Exception { - writeACLFile(null, - "ACL ALLOW admin ACCESS MANAGEMENT", - "ACL ALLOW admin ALL METHOD component='ServerInformation'"); + writeACLFile("ACL ALLOW admin ACCESS MANAGEMENT", + "ACL ALLOW admin ALL METHOD component='ServerInformation'"); } public void testServerInformationAllMethodPermissions() throws Exception diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java index 00c85e80c8..0e8f3cb7d8 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java @@ -18,12 +18,6 @@ */ package org.apache.qpid.server.security.acl; -import org.apache.qpid.AMQException; -import org.apache.qpid.client.AMQDestination; -import org.apache.qpid.client.AMQSession; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.url.URLSyntaxException; - import javax.jms.Connection; import javax.jms.Destination; import javax.jms.JMSException; @@ -37,8 +31,14 @@ import javax.jms.Topic; import javax.jms.TopicSubscriber; import javax.naming.NamingException; +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.url.URLSyntaxException; + /** - * Tests the V2 ACLs. The tests perform basic AMQP operations like creating queues or excahnges and publishing and consuming messages, using + * Tests the V2 ACLs. The tests perform basic AMQP operations like creating queues or exchanges and publishing and consuming messages, using * JMS to contact the broker. */ public class ExternalACLTest extends AbstractACLTestCase @@ -46,65 +46,113 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpAccessAuthorizedSuccess() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST"); } public void testAccessAuthorizedSuccess() throws Exception { + Connection conn = getConnection("test", "client", "guest"); + conn.close(); + } + + public void setUpAccessNoRightsFailure() throws Exception + { + writeACLFile("ACL DENY-LOG client ACCESS VIRTUALHOST"); + } + + public void testAccessNoRightsFailure() throws Exception + { try { - Connection conn = getConnection("test", "client", "guest"); - Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); - conn.start(); + getConnection("test", "client", "guest"); + fail("Connection was created."); + } + catch (JMSException e) + { + assertAccessDeniedException(e); + } + } - //Do something to show connection is active. - sess.rollback(); + private void assertAccessDeniedException(JMSException e) + { + assertEquals("Unexpected exception message", "Error creating connection: Permission denied: test", e.getMessage()); + + // JMSException -> linkedException -> cause = AMQException (403 or 320) + Exception linkedException = e.getLinkedException(); + assertNotNull("There was no linked exception", linkedException); + Throwable cause = linkedException.getCause(); + assertNotNull("Cause was null", cause); + assertTrue("Wrong linked exception type", cause instanceof AMQException); + AMQConstant errorCode = isBroker010() ? AMQConstant.CONNECTION_FORCED : AMQConstant.ACCESS_REFUSED; + assertEquals("Incorrect error code received", errorCode, ((AMQException) cause).getErrorCode()); + } + + public void setUpAccessVirtualHostWithName() throws Exception + { + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST name='test'", "ACL DENY-LOG guest ACCESS VIRTUALHOST name='test'", + "ACL ALLOW-LOG server ACCESS VIRTUALHOST name='*'"); + } - conn.close(); + public void testAccessVirtualHostWithName() throws Exception + { + Connection conn = getConnection("test", "client", "guest"); + conn.close(); + + try + { + getConnection("test", "guest", "guest"); + fail("Access should be denied"); } - catch (Exception e) + catch (JMSException e) { - fail("Connection was not created due to:" + e); + assertAccessDeniedException(e); } + + Connection conn2 = getConnection("test", "server", "guest"); + conn2.close(); } - public void setUpAccessNoRightsFailure() throws Exception + public void setUpClientCreateVirtualHostQueue() throws Exception { - writeACLFile("test", "ACL DENY-LOG client ACCESS VIRTUALHOST"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE virtualhost_name='test'", + "ACL ALLOW-LOG client CONSUME QUEUE", + "ACL ALLOW-LOG client BIND EXCHANGE", + "ACL ALLOW-LOG guest ACCESS VIRTUALHOST", + "ACL DENY-LOG guest CREATE QUEUE virtualhost_name='test'"); } - public void testAccessNoRightsFailure() throws Exception + public void testClientCreateVirtualHostQueue() throws NamingException, JMSException, AMQException, Exception { + Connection conn = getConnection("test", "client", "guest"); + Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination dest = sess.createQueue(getTestQueueName()); + sess.createConsumer(dest); + conn.close(); + try { - Connection conn = getConnection("test", "guest", "guest"); - Session sess = conn.createSession(true, Session.SESSION_TRANSACTED); - conn.start(); - sess.rollback(); + conn = getConnection("test", "guest", "guest"); + sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE); + sess.createConsumer(dest); - fail("Connection was created."); + fail("Queue creation for user 'guest' is denied"); } catch (JMSException e) { - // JMSException -> linkedException -> cause = AMQException (403 or 320) - Exception linkedException = e.getLinkedException(); - assertNotNull("There was no linked exception", linkedException); - Throwable cause = linkedException.getCause(); - assertNotNull("Cause was null", cause); - assertTrue("Wrong linked exception type", cause instanceof AMQException); - AMQConstant errorCode = isBroker010() ? AMQConstant.CONNECTION_FORCED : AMQConstant.ACCESS_REFUSED; - assertEquals("Incorrect error code received", errorCode, ((AMQException) cause).getErrorCode()); + check403Exception(e.getLinkedException()); } } + public void setUpClientDeleteQueueSuccess() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client CREATE QUEUE durable=\"true\"" , - "ACL ALLOW-LOG client CONSUME QUEUE name=\"clientid:kipper\"", - "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper", - "ACL ALLOW-LOG client DELETE QUEUE durable=\"true\"", - "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE durable=\"true\"", + "ACL ALLOW-LOG client CONSUME QUEUE name=\"clientid:kipper\"" , + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper", + "ACL ALLOW-LOG client DELETE QUEUE durable=\"true\"", + "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper"); } public void testClientDeleteQueueSuccess() throws Exception @@ -128,12 +176,12 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientDeleteQueueFailure() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client CREATE QUEUE durable=\"true\"" , - "ACL ALLOW-LOG client CONSUME QUEUE name=\"clientid:kipper\"", - "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper", - "ACL DENY-LOG client DELETE QUEUE durable=\"true\"", - "ACL DENY-LOG client UNBIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE durable=\"true\"", + "ACL ALLOW-LOG client CONSUME QUEUE name=\"clientid:kipper\"" , + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper", + "ACL DENY-LOG client DELETE QUEUE durable=\"true\"", + "ACL DENY-LOG client UNBIND EXCHANGE name=\"amq.topic\" durable=true routingKey=kipper"); } public void testClientDeleteQueueFailure() throws Exception @@ -177,10 +225,10 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientConsumeFromNamedQueueValid() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client CREATE QUEUE name=\"example.RequestQueue\"" , - "ACL ALLOW-LOG client CONSUME QUEUE name=\"example.RequestQueue\"", - "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\""); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE name=\"example.RequestQueue\"", + "ACL ALLOW-LOG client CONSUME QUEUE name=\"example.RequestQueue\"" , + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\""); } @@ -197,10 +245,10 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientConsumeFromNamedQueueFailure() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client CREATE QUEUE" , - "ACL ALLOW-LOG client BIND EXCHANGE", - "ACL DENY-LOG client CONSUME QUEUE name=\"IllegalQueue\""); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE", + "ACL ALLOW-LOG client BIND EXCHANGE" , + "ACL DENY-LOG client CONSUME QUEUE name=\"IllegalQueue\""); } public void testClientConsumeFromNamedQueueFailure() throws NamingException, Exception @@ -224,11 +272,11 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientCreateTemporaryQueueSuccess() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client CREATE QUEUE temporary=\"true\"" , - "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" temporary=true", - "ACL ALLOW-LOG client DELETE QUEUE temporary=\"true\"", - "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.direct\" temporary=true"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE temporary=\"true\"", + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" temporary=true" , + "ACL ALLOW-LOG client DELETE QUEUE temporary=\"true\"", + "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.direct\" temporary=true"); } public void testClientCreateTemporaryQueueSuccess() throws JMSException, URLSyntaxException, Exception @@ -243,8 +291,8 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientCreateTemporaryQueueFailed() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL DENY-LOG client CREATE QUEUE temporary=\"true\""); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL DENY-LOG client CREATE QUEUE temporary=\"true\""); } public void testClientCreateTemporaryQueueFailed() throws NamingException, Exception @@ -268,9 +316,8 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientCreateNamedQueueFailure() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client CREATE QUEUE name=\"ValidQueue\"", - "ACL ALLOW-LOG client CONSUME QUEUE"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE name=\"ValidQueue\""); } public void testClientCreateNamedQueueFailure() throws NamingException, JMSException, AMQException, Exception @@ -294,10 +341,10 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientPublishUsingTransactionSuccess() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client CREATE QUEUE" , - "ACL ALLOW-LOG client BIND EXCHANGE", - "ACL ALLOW-LOG client PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\""); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client CREATE QUEUE", + "ACL ALLOW-LOG client BIND EXCHANGE" , + "ACL ALLOW-LOG client PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\""); } public void testClientPublishUsingTransactionSuccess() throws Exception @@ -329,19 +376,18 @@ public class ExternalACLTest extends AbstractACLTestCase // We tolerate a dependency from this test to that file because its // contents are expected to change rarely. - writeACLFile("test", "ACL ALLOW-LOG messaging-users ACCESS VIRTUALHOST", - "# Server side", - "ACL ALLOW-LOG server CREATE QUEUE name=\"example.RequestQueue\"" , - "ACL ALLOW-LOG server BIND EXCHANGE", - "ACL ALLOW-LOG server PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"TempQueue*\"", - "ACL ALLOW-LOG server CONSUME QUEUE name=\"example.RequestQueue\"", - "# Client side", - "ACL ALLOW-LOG client PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\"", - "ACL ALLOW-LOG client CONSUME QUEUE temporary=true", - "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" temporary=true", - "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.direct\" temporary=true", - "ACL ALLOW-LOG client CREATE QUEUE temporary=true", - "ACL ALLOW-LOG client DELETE QUEUE temporary=true"); + writeACLFile("ACL ALLOW-LOG messaging-users ACCESS VIRTUALHOST", "# Server side", + "ACL ALLOW-LOG server CREATE QUEUE name=\"example.RequestQueue\"", + "ACL ALLOW-LOG server BIND EXCHANGE" , + "ACL ALLOW-LOG server PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"TempQueue*\"", + "ACL ALLOW-LOG server CONSUME QUEUE name=\"example.RequestQueue\"", + "# Client side", + "ACL ALLOW-LOG client PUBLISH EXCHANGE name=\"amq.direct\" routingKey=\"example.RequestQueue\"", + "ACL ALLOW-LOG client CONSUME QUEUE temporary=true", + "ACL ALLOW-LOG client BIND EXCHANGE name=\"amq.direct\" temporary=true", + "ACL ALLOW-LOG client UNBIND EXCHANGE name=\"amq.direct\" temporary=true", + "ACL ALLOW-LOG client CREATE QUEUE temporary=true", + "ACL ALLOW-LOG client DELETE QUEUE temporary=true"); } @@ -386,9 +432,9 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpClientDeleteQueueSuccessWithOnlyAllPermissions() throws Exception { - writeACLFile("test", "ACL ALLOW-LOG client ACCESS VIRTUALHOST", - "ACL ALLOW-LOG client ALL QUEUE", - "ACL ALLOW-LOG client ALL EXCHANGE"); + writeACLFile("ACL ALLOW-LOG client ACCESS VIRTUALHOST", + "ACL ALLOW-LOG client ALL QUEUE", + "ACL ALLOW-LOG client ALL EXCHANGE"); } public void testClientDeleteQueueSuccessWithOnlyAllPermissions() throws Exception @@ -412,7 +458,7 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpFirewallAllow() throws Exception { - writeACLFile("test", "ACL ALLOW client ACCESS VIRTUALHOST from_network=\"127.0.0.1\""); + writeACLFile("ACL ALLOW client ACCESS VIRTUALHOST from_network=\"127.0.0.1\""); } public void testFirewallAllow() throws Exception @@ -423,7 +469,7 @@ public class ExternalACLTest extends AbstractACLTestCase public void setUpFirewallDeny() throws Exception { - writeACLFile("test", "ACL DENY client ACCESS VIRTUALHOST from_network=\"127.0.0.1\""); + writeACLFile("ACL DENY client ACCESS VIRTUALHOST from_network=\"127.0.0.1\""); } public void testFirewallDeny() throws Exception diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java index 36e86fbe7b..d89f5cc66e 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/MessageStoreTest.java @@ -185,6 +185,12 @@ public class MessageStoreTest extends QpidTestCase try { _virtualHost = BrokerTestHelper.createVirtualHost(new VirtualHostRegistry(new EventLogger()), getVirtualHostModel()); + when(_virtualHostModel.getId()).thenReturn(_virtualHost.getId()); + + ConfiguredObjectRecord objectRecord = mock(ConfiguredObjectRecord.class); + when(objectRecord.getId()).thenReturn(_virtualHost.getId()); + when(objectRecord.getType()).thenReturn(org.apache.qpid.server.model.VirtualHost.class.getSimpleName()); + when(_virtualHostModel.asObjectRecord()).thenReturn(objectRecord); } catch (Exception e) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java index 06b7091a47..cae6e29b96 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/QuotaMessageStore.java @@ -26,6 +26,7 @@ import java.util.concurrent.atomic.AtomicLong; import org.apache.qpid.server.message.EnqueueableMessage; import org.apache.qpid.server.message.MessageContentSource; +import org.apache.qpid.server.model.ConfiguredObject; public class QuotaMessageStore extends NullMessageStore @@ -81,7 +82,7 @@ public class } @Override - public void recoverMessageStore(MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) + public void recoverMessageStore(ConfiguredObject<?> parent, MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) { _stateManager.attainState(State.ACTIVATING); _stateManager.attainState(State.ACTIVE); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java index 44a5252355..8ec576c7ca 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/SlowMessageStore.java @@ -29,7 +29,7 @@ import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import org.apache.qpid.server.message.EnqueueableMessage; -import org.apache.qpid.server.plugin.DurableConfigurationStoreFactory; +import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.plugin.MessageStoreFactory; public class SlowMessageStore implements MessageStore, DurableConfigurationStore @@ -64,9 +64,9 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore } @Override - public void recoverConfigurationStore(ConfigurationRecoveryHandler recoveryHandler) + public void recoverConfigurationStore(ConfiguredObject<?> parent, ConfigurationRecoveryHandler recoveryHandler) { - _realDurableConfigurationStore.recoverConfigurationStore(recoveryHandler); + _realDurableConfigurationStore.recoverConfigurationStore(parent, recoveryHandler); } private void configureDelays(Map<String, Object> delays) @@ -183,7 +183,6 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore _realDurableConfigurationStore.closeConfigurationStore(); } - @Override public <M extends StorableMessageMetaData> StoredMessage<M> addMessage(M metaData) { @@ -191,39 +190,24 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore } @Override - public void create(UUID id, String type, Map<String, Object> attributes) throws StoreException + public void create(ConfiguredObjectRecord record) throws StoreException { doPreDelay("create"); - _realDurableConfigurationStore.create(id, type, attributes); + _realDurableConfigurationStore.create(record); doPostDelay("create"); } - @Override - public void remove(UUID id, String type) throws StoreException - { - doPreDelay("remove"); - _realDurableConfigurationStore.remove(id, type); - doPostDelay("remove"); - } @Override - public UUID[] removeConfiguredObjects(final UUID... objects) throws StoreException + public UUID[] remove(final ConfiguredObjectRecord... objects) throws StoreException { doPreDelay("remove"); - UUID[] removed = _realDurableConfigurationStore.removeConfiguredObjects(objects); + UUID[] removed = _realDurableConfigurationStore.remove(objects); doPostDelay("remove"); return removed; } @Override - public void update(UUID id, String type, Map<String, Object> attributes) throws StoreException - { - doPreDelay("update"); - _realDurableConfigurationStore.update(id, type, attributes); - doPostDelay("update"); - } - - @Override public void update(boolean createIfNecessary, ConfiguredObjectRecord... records) throws StoreException { doPreDelay("update"); @@ -310,9 +294,9 @@ public class SlowMessageStore implements MessageStore, DurableConfigurationStore } @Override - public void recoverMessageStore(MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) + public void recoverMessageStore(ConfiguredObject<?> parent, MessageStoreRecoveryHandler messageRecoveryHandler, TransactionLogRecoveryHandler transactionLogRecoveryHandler) { - _realMessageStore.recoverMessageStore(messageRecoveryHandler, transactionLogRecoveryHandler); + _realMessageStore.recoverMessageStore(parent, messageRecoveryHandler, transactionLogRecoveryHandler); } @Override diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java index ec389e55f1..da9466e9a9 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java @@ -63,8 +63,7 @@ public class BrokerACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER", "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER", "ACL DENY-LOG ALL ALL"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java index b63df34b98..f20d9dfa47 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/ExchangeRestACLTest.java @@ -47,8 +47,7 @@ public class ExchangeRestACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE QUEUE", "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE EXCHANGE", "ACL DENY-LOG " + DENIED_USER + " CREATE EXCHANGE", diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java index aff1eac9cf..e6d5f8b1e0 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java @@ -94,8 +94,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void testCreateGroup() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " CREATE GROUP", "ACL DENY-LOG " + DENIED_GROUP + " CREATE GROUP"); @@ -121,8 +120,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void testDeleteGroup() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " DELETE GROUP", "ACL DENY-LOG " + DENIED_GROUP + " DELETE GROUP"); @@ -148,8 +146,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void testUpdateGroupAddMember() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE GROUP", "ACL DENY-LOG " + DENIED_GROUP + " UPDATE GROUP"); @@ -169,8 +166,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void testUpdateGroupDeleteMember() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE GROUP", "ACL DENY-LOG " + DENIED_GROUP + " UPDATE GROUP"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java index 7d94ee27ad..dd6c7d6a28 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/LogViewerACLTest.java @@ -38,8 +38,7 @@ public class LogViewerACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " ACCESS_LOGS BROKER", "ACL DENY-LOG " + DENIED_USER + " ACCESS_LOGS BROKER", "ACL DENY-LOG ALL ALL"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java index 52f70a5dd2..b712327329 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/QueueRestACLTest.java @@ -42,8 +42,7 @@ public class QueueRestACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " CREATE QUEUE", "ACL DENY-LOG " + DENIED_USER + " CREATE QUEUE", "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE QUEUE", diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java index b23e44a4d3..4b4854e4b3 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserPreferencesRestACLTest.java @@ -76,7 +76,7 @@ public class UserPreferencesRestACLTest extends QpidRestTestCase super.customizeConfiguration(); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER); - AbstractACLTestCase.writeACLFileUtil(this, null, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_USER + " UPDATE USER", "ACL DENY-LOG " + DENIED_USER + " UPDATE USER", "ACL DENY-LOG ALL ALL"); TestBrokerConfiguration brokerConfiguration = getBrokerConfiguration(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java index d2f0401db5..7c5042c750 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java @@ -96,8 +96,7 @@ public class UserRestACLTest extends QpidRestTestCase public void testAddUser() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " CREATE USER", "ACL DENY-LOG " + DENIED_GROUP + " CREATE USER"); @@ -121,8 +120,7 @@ public class UserRestACLTest extends QpidRestTestCase public void testDeleteUser() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " DELETE USER", "ACL DENY-LOG " + DENIED_GROUP + " DELETE USER"); @@ -142,8 +140,7 @@ public class UserRestACLTest extends QpidRestTestCase public void testUpdateUser() throws Exception { - AbstractACLTestCase.writeACLFileUtil(this, null, - "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", + AbstractACLTestCase.writeACLFileUtil(this, "ACL ALLOW-LOG ALL ACCESS MANAGEMENT", "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE USER", "ACL DENY-LOG " + DENIED_GROUP + " UPDATE USER"); |
