summaryrefslogtreecommitdiff
path: root/qpid/java/broker
diff options
context:
space:
mode:
authorAlex Rudyy <orudyy@apache.org>2013-02-27 17:50:17 +0000
committerAlex Rudyy <orudyy@apache.org>2013-02-27 17:50:17 +0000
commit119554c72479af7ecaeafd12d0f840c8fb50f415 (patch)
treecd59d2081f84dc36158986d573225554c273d19c /qpid/java/broker
parent466d209aea03a17c508b887682ab769ba72f9e44 (diff)
downloadqpid-python-119554c72479af7ecaeafd12d0f840c8fb50f415.tar.gz
QPID-4596: Add java broker ability to create/delete/update virtual hosts, authentication providers and ports via REST interfaces
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1450881 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker')
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java14
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java4
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java3
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java2
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/IntegrityViolationException.java37
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java46
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java11
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java80
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java12
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java29
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java83
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java186
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java151
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/util/ParameterizedTypeImpl.java73
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java2
15 files changed, 564 insertions, 169 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java
index 813702d0a6..3022898300 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/StoreConfigurationChangeListener.java
@@ -29,10 +29,12 @@ import java.util.UUID;
import org.apache.qpid.server.configuration.ConfigurationEntry;
import org.apache.qpid.server.configuration.ConfigurationEntryStore;
+import org.apache.qpid.server.model.AuthenticationProvider;
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.Model;
+import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.VirtualHost;
@@ -123,6 +125,18 @@ public class StoreConfigurationChangeListener implements ConfigurationChangeList
{
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());
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java
index 417f6036ab..c7d3aa76af 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AuthenticationProvider.java
@@ -39,7 +39,8 @@ public interface AuthenticationProvider extends ConfiguredObject
public static final String TIME_TO_LIVE = "timeToLive";
public static final String CREATED = "created";
public static final String UPDATED = "updated";
- public static final String TYPE = "type";
+ public static final String CATEGORY = "category";
+ public static final String TYPE = "authenticationProviderType";
public static final Collection<String> AVAILABLE_ATTRIBUTES =
Collections.unmodifiableList(
@@ -52,6 +53,7 @@ public interface AuthenticationProvider extends ConfiguredObject
TIME_TO_LIVE,
CREATED,
UPDATED,
+ CATEGORY,
TYPE));
//children
Collection<VirtualHostAlias> getVirtualHostPortBindings();
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java
index bd7da962ba..d20c709e90 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java
@@ -30,8 +30,7 @@ public interface ConfigurationChangeListener
* @param newState the state after the change
*/
void stateChanged(ConfiguredObject object, State oldState, State newState);
-
-
+
void childAdded(ConfiguredObject object, ConfiguredObject child);
void childRemoved(ConfiguredObject object, ConfiguredObject child);
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java
index d567a3aa44..45e743dbca 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java
@@ -257,4 +257,6 @@ public interface ConfiguredObject
<C extends ConfiguredObject> C createChild(Class<C> childClass,
Map<String, Object> attributes,
ConfiguredObject... otherParents);
+
+ void setAttributes(Map<String, Object> attributes) throws IllegalStateException, AccessControlException, IllegalArgumentException;
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/IntegrityViolationException.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/IntegrityViolationException.java
new file mode 100644
index 0000000000..def450640a
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/IntegrityViolationException.java
@@ -0,0 +1,37 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.model;
+
+public class IntegrityViolationException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ public IntegrityViolationException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ public IntegrityViolationException(String message)
+ {
+ super(message);
+ }
+
+}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
index 73e1f1e970..6b6cce3ffa 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java
@@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.Callable;
import org.apache.qpid.server.model.ConfigurationChangeListener;
import org.apache.qpid.server.model.ConfiguredObject;
@@ -55,14 +56,7 @@ abstract class AbstractAdapter implements ConfiguredObject
_id = id;
if (attributes != null)
{
- Collection<String> names = getAttributeNames();
- for (String name : names)
- {
- if (attributes.containsKey(name))
- {
- _attributes.put(name, attributes.get(name));
- }
- }
+ _attributes.putAll(attributes);
}
if (defaults != null)
{
@@ -319,4 +313,40 @@ abstract class AbstractAdapter implements ConfiguredObject
return _taskExecutor;
}
+ @Override
+ public void setAttributes(final Map<String, Object> attributes) throws IllegalStateException, AccessControlException, IllegalArgumentException
+ {
+ if (getTaskExecutor().isTaskExecutorThread())
+ {
+ changeAttributes(attributes);
+ }
+ else
+ {
+ getTaskExecutor().submitAndWait(new Callable<Void>()
+ {
+
+ @Override
+ public Void call() throws Exception
+ {
+ AbstractAdapter.this.setAttributes(attributes);
+ return null;
+ }
+ });
+ }
+
+ }
+
+ protected void changeAttributes(final Map<String, Object> attributes)
+ {
+ for (Map.Entry<String, Object> attributeEntry : attributes.entrySet())
+ {
+ String name = attributeEntry.getKey();
+ Object desired = attributeEntry.getValue();
+ Object expected = getAttribute(name);
+ if (changeAttribute(name, expected, desired))
+ {
+ attributeSet(name, expected, desired);
+ }
+ }
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java
index 2f7e89bb2b..8ade1369ac 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java
@@ -45,6 +45,7 @@ import org.apache.qpid.server.model.TrustStore;
import org.apache.qpid.server.configuration.updater.TaskExecutor;
import org.apache.qpid.server.protocol.AmqpProtocolVersion;
import org.apache.qpid.server.protocol.MultiVersionProtocolEngineFactory;
+import org.apache.qpid.server.util.MapValueConverter;
import org.apache.qpid.ssl.SSLContextFactory;
import org.apache.qpid.transport.NetworkTransportConfiguration;
import org.apache.qpid.transport.network.IncomingNetworkTransport;
@@ -189,6 +190,16 @@ public class AmqpPortAdapter extends PortAdapter
return null;
}
+ @Override
+ protected void changeAttributes(Map<String, Object> attributes)
+ {
+ if (_transport != null)
+ {
+ throw new IllegalStateException("Port " + getAttribute(PORT)
+ + " is already opened. Start broker in management mode to change a port");
+ }
+ super.changeAttributes(MapValueConverter.convert(attributes, ATTRIBUTE_TYPES));
+ }
class ServerNetworkTransportConfiguration implements NetworkTransportConfiguration
{
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
index ac4b0255d5..afab8a4900 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java
@@ -38,13 +38,18 @@ 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.IllegalStateTransitionException;
+import org.apache.qpid.server.model.IntegrityViolationException;
import org.apache.qpid.server.model.LifetimePolicy;
import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider;
+import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.Statistics;
import org.apache.qpid.server.model.UUIDGenerator;
import org.apache.qpid.server.model.User;
import org.apache.qpid.server.model.VirtualHostAlias;
+import org.apache.qpid.server.plugin.AuthenticationManagerFactory;
+import org.apache.qpid.server.plugin.QpidServiceLoader;
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.configuration.updater.TaskExecutor;
import org.apache.qpid.server.security.SubjectCreator;
import org.apache.qpid.server.security.access.Operation;
@@ -59,19 +64,19 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
{
private static final Logger LOGGER = Logger.getLogger(AuthenticationProviderAdapter.class);
- private final T _authManager;
+ protected T _authManager;
protected final Broker _broker;
private GroupPrincipalAccessor _groupAccessor;
- private Object _type;
+ protected String _category;
private AuthenticationProviderAdapter(UUID id, Broker broker, final T authManager, Map<String, Object> attributes)
{
super(id, null, attributes, broker.getTaskExecutor());
_authManager = authManager;
_broker = broker;
- _type = authManager instanceof PrincipalDatabaseAuthenticationManager? PrincipalDatabaseAuthenticationManager.class.getSimpleName() : AuthenticationManager.class.getSimpleName() ;
+ _category = authManager instanceof PrincipalDatabaseAuthenticationManager? PrincipalDatabaseAuthenticationManager.class.getSimpleName() : AuthenticationManager.class.getSimpleName() ;
addParent(Broker.class, broker);
}
@@ -157,9 +162,9 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
@Override
public Object getAttribute(String name)
{
- if(TYPE.equals(name))
+ if(CATEGORY.equals(name))
{
- return _type;
+ return _category;
}
else if(CREATED.equals(name))
{
@@ -204,6 +209,22 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
{
if(desiredState == State.DELETED)
{
+ String providerName = getName();
+
+ // verify that provider is not in use
+ if (providerName.equals(_broker.getAttribute(Broker.DEFAULT_AUTHENTICATION_PROVIDER)))
+ {
+ throw new IntegrityViolationException("Authentication provider '" + providerName + "' is set as default and cannot be deleted");
+ }
+ Collection<Port> ports = new ArrayList<Port>(_broker.getPorts());
+ for (Port port : ports)
+ {
+ if (providerName.equals(port.getAttribute(Port.AUTHENTICATION_MANAGER)))
+ {
+ throw new IntegrityViolationException("Authentication provider '" + providerName + "' is set on port " + port.getName());
+ }
+ }
+
return true;
}
else if(desiredState == State.ACTIVE)
@@ -234,6 +255,21 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
_groupAccessor = groupAccessor;
}
+ public AuthenticationManager createAuthenticationManager(Map<String, Object> attributes)
+ {
+ QpidServiceLoader<AuthenticationManagerFactory> loader = new QpidServiceLoader<AuthenticationManagerFactory>();
+ Iterable<AuthenticationManagerFactory> factories = loader.atLeastOneInstanceOf(AuthenticationManagerFactory.class);
+ for (AuthenticationManagerFactory factory : factories)
+ {
+ AuthenticationManager manager = factory.createInstance(attributes);
+ if (manager != null)
+ {
+ return manager;
+ }
+ }
+ return null;
+ }
+
public static class SimpleAuthenticationProviderAdapter extends AuthenticationProviderAdapter<AuthenticationManager>
{
@@ -250,6 +286,23 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
{
throw new UnsupportedOperationException();
}
+
+ @Override
+ protected void changeAttributes(Map<String, Object> attributes)
+ {
+ AuthenticationManager manager = createAuthenticationManager(attributes);
+ if (manager == null)
+ {
+ throw new IllegalConfigurationException("Cannot create authentication manager from " + attributes);
+ }
+ if (manager instanceof PrincipalDatabaseAuthenticationManager)
+ {
+ throw new IllegalConfigurationException("Cannot change the category of the authentication provider");
+ }
+ _authManager = manager;
+ super.changeAttributes(attributes);
+ }
+
}
public static class PrincipalDatabaseAuthenticationManagerAdapter
@@ -377,11 +430,26 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana
}
}
+ @Override
+ protected void changeAttributes(Map<String, Object> attributes)
+ {
+ AuthenticationManager manager = createAuthenticationManager(attributes);
+ if (manager == null)
+ {
+ throw new IllegalConfigurationException("Cannot create authentication manager from " + attributes);
+ }
+ if (!(manager instanceof PrincipalDatabaseAuthenticationManager))
+ {
+ throw new IllegalConfigurationException("Cannot change the category of the authentication provider");
+ }
+ _authManager = (PrincipalDatabaseAuthenticationManager)manager;
+ super.changeAttributes(attributes);
+ }
+
private class PrincipalAdapter extends AbstractAdapter implements User
{
private final Principal _user;
-
public PrincipalAdapter(Principal user, TaskExecutor taskExecutor)
{
super(UUIDGenerator.generateUserUUID(PrincipalDatabaseAuthenticationManagerAdapter.this.getName(), user.getName()), taskExecutor);
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
index 533ecfe937..197f70a884 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java
@@ -20,6 +20,7 @@
*/
package org.apache.qpid.server.model.adapter;
+import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.security.AccessControlException;
@@ -69,7 +70,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
private static final Logger LOGGER = Logger.getLogger(BrokerAdapter.class);
@SuppressWarnings("serial")
- public static final Map<String, Class<?>> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Class<?>>(){{
+ public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{
put(ALERT_THRESHOLD_MESSAGE_AGE, Long.class);
put(ALERT_THRESHOLD_MESSAGE_COUNT, Long.class);
put(ALERT_THRESHOLD_QUEUE_DEPTH, Long.class);
@@ -312,7 +313,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
{
synchronized (_vhostAdapters)
{
- _vhostAdapters.remove(vhost);
+ _vhostAdapters.remove(vhost.getName());
}
vhost.removeChangeListener(this);
return true;
@@ -410,7 +411,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
return Collections.emptySet();
}
- //TODO: ACL
@SuppressWarnings("unchecked")
@Override
public <C extends ConfiguredObject> C addChild(Class<C> childClass, Map<String, Object> attributes, ConfiguredObject... otherParents)
@@ -916,4 +916,10 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat
{
return super.getTaskExecutor();
}
+
+ @Override
+ protected void changeAttributes(Map<String, Object> attributes)
+ {
+ super.changeAttributes(MapValueConverter.convert(attributes, ATTRIBUTE_TYPES));
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
index c4a531c923..298ff05dd1 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java
@@ -21,12 +21,15 @@
package org.apache.qpid.server.model.adapter;
+import java.lang.reflect.Type;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import org.apache.qpid.server.model.AuthenticationProvider;
@@ -41,10 +44,27 @@ import org.apache.qpid.server.model.Statistics;
import org.apache.qpid.server.model.Transport;
import org.apache.qpid.server.model.VirtualHost;
import org.apache.qpid.server.model.VirtualHostAlias;
+import org.apache.qpid.server.util.MapValueConverter;
+import org.apache.qpid.server.util.ParameterizedTypeImpl;
import org.apache.qpid.server.configuration.updater.TaskExecutor;
public class PortAdapter extends AbstractAdapter implements Port
{
+ @SuppressWarnings("serial")
+ public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{
+ put(NAME, String.class);
+ put(PROTOCOLS, new ParameterizedTypeImpl(Set.class, Protocol.class));
+ put(TRANSPORTS, new ParameterizedTypeImpl(Set.class, Transport.class));
+ put(PORT, Integer.class);
+ put(TCP_NO_DELAY, Boolean.class);
+ put(RECEIVE_BUFFER_SIZE, Integer.class);
+ put(SEND_BUFFER_SIZE, Integer.class);
+ put(NEED_CLIENT_AUTH, Boolean.class);
+ put(WANT_CLIENT_AUTH, Boolean.class);
+ put(BINDING_ADDRESS, String.class);
+ put(STATE, State.class);
+ put(AUTHENTICATION_MANAGER, String.class);
+ }});
private final Broker _broker;
private AuthenticationProvider _authenticationProvider;
@@ -56,11 +76,9 @@ public class PortAdapter extends AbstractAdapter implements Port
*/
public PortAdapter(UUID id, Broker broker, Map<String, Object> attributes, Map<String, Object> defaults, TaskExecutor taskExecutor)
{
- super(id, defaults, attributes, taskExecutor);
+ super(id, defaults, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), taskExecutor);
_broker = broker;
-
addParent(Broker.class, broker);
-
}
@Override
@@ -308,4 +326,9 @@ public class PortAdapter extends AbstractAdapter implements Port
_authenticationProvider = authenticationProvider;
}
+ @Override
+ protected void changeAttributes(Map<String, Object> attributes)
+ {
+ super.changeAttributes(MapValueConverter.convert(attributes, ATTRIBUTE_TYPES));
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java
index b7441b9f3b..9d501115b7 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java
@@ -35,7 +35,6 @@ import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.Protocol;
import org.apache.qpid.server.model.Protocol.ProtocolType;
-import org.apache.qpid.server.model.State;
import org.apache.qpid.server.model.Transport;
import org.apache.qpid.server.util.MapValueConverter;
@@ -78,10 +77,8 @@ public class PortFactory
_defaultProtocols = Collections.unmodifiableCollection(defaultProtocols);
}
- public Port createPort(UUID id, Broker broker, Map<String, Object> objectAttributes)
+ public Port createPort(UUID id, Broker broker, Map<String, Object> attributes)
{
- Map<String, Object> attributes = retrieveAttributes(objectAttributes);
-
final Port port;
Map<String, Object> defaults = new HashMap<String, Object>();
defaults.put(Port.TRANSPORTS, Collections.singleton(DEFAULT_TRANSPORT));
@@ -90,7 +87,8 @@ public class PortFactory
{
throw new IllegalConfigurationException("Port attribute is not specified for port: " + attributes);
}
- if (isAmqpProtocol(attributes))
+ Set<Protocol> protocols = MapValueConverter.getEnumSetAttribute(Port.PROTOCOLS, attributes, Protocol.class);
+ if (isAmqpProtocol(protocols, attributes))
{
Object binding = attributes.get(Port.BINDING_ADDRESS);
if (binding == null)
@@ -109,8 +107,6 @@ public class PortFactory
}
else
{
- @SuppressWarnings("unchecked")
- Collection<Protocol> protocols = (Collection<Protocol>)attributes.get(Port.PROTOCOLS);
if (protocols.size() > 1)
{
throw new IllegalConfigurationException("Only one protocol can be used on non AMQP port");
@@ -122,77 +118,8 @@ public class PortFactory
return port;
}
- private Map<String, Object> retrieveAttributes(Map<String, Object> objectAttributes)
- {
- Map<String, Object> attributes = new HashMap<String, Object>(objectAttributes);
-
- if (objectAttributes.containsKey(Port.PROTOCOLS))
- {
- final Set<Protocol> protocolSet = MapValueConverter.getEnumSetAttribute(Port.PROTOCOLS, objectAttributes, Protocol.class);
- attributes.put(Port.PROTOCOLS, protocolSet);
- }
-
- if (objectAttributes.containsKey(Port.TRANSPORTS))
- {
- final Set<Transport> transportSet = MapValueConverter.getEnumSetAttribute(Port.TRANSPORTS, objectAttributes,
- Transport.class);
- attributes.put(Port.TRANSPORTS, transportSet);
- }
-
- if (objectAttributes.containsKey(Port.PORT))
- {
- Integer port = MapValueConverter.getIntegerAttribute(Port.PORT, objectAttributes);
- attributes.put(Port.PORT, port);
- }
-
- if (objectAttributes.containsKey(Port.TCP_NO_DELAY))
- {
- boolean tcpNoDelay = MapValueConverter.getBooleanAttribute(Port.TCP_NO_DELAY, objectAttributes);
- attributes.put(Port.TCP_NO_DELAY, tcpNoDelay);
- }
-
- if (objectAttributes.containsKey(Port.RECEIVE_BUFFER_SIZE))
- {
- int receiveBufferSize = MapValueConverter.getIntegerAttribute(Port.RECEIVE_BUFFER_SIZE, objectAttributes);
- attributes.put(Port.RECEIVE_BUFFER_SIZE, receiveBufferSize);
- }
-
- if (objectAttributes.containsKey(Port.SEND_BUFFER_SIZE))
- {
- int sendBufferSize = MapValueConverter.getIntegerAttribute(Port.SEND_BUFFER_SIZE, objectAttributes);
- attributes.put(Port.SEND_BUFFER_SIZE, sendBufferSize);
- }
-
- if (objectAttributes.containsKey(Port.NEED_CLIENT_AUTH))
- {
- boolean needClientAuth = MapValueConverter.getBooleanAttribute(Port.NEED_CLIENT_AUTH, objectAttributes);
- attributes.put(Port.NEED_CLIENT_AUTH, needClientAuth);
- }
-
- if (objectAttributes.containsKey(Port.WANT_CLIENT_AUTH))
- {
- boolean wantClientAuth = MapValueConverter.getBooleanAttribute(Port.WANT_CLIENT_AUTH, objectAttributes);
- attributes.put(Port.WANT_CLIENT_AUTH, wantClientAuth);
- }
-
- if (objectAttributes.containsKey(Port.BINDING_ADDRESS))
- {
- String binding = MapValueConverter.getStringAttribute(Port.BINDING_ADDRESS, objectAttributes);
- attributes.put(Port.BINDING_ADDRESS, binding);
- }
-
- if (objectAttributes.containsKey(Port.STATE))
- {
- State state = MapValueConverter.getEnumAttribute(State.class, Port.STATE, objectAttributes);
- attributes.put(Port.STATE, state);
- }
- return attributes;
- }
-
- private boolean isAmqpProtocol(Map<String, Object> portAttributes)
+ private boolean isAmqpProtocol(Set<Protocol> protocols, Map<String, Object> portAttributes)
{
- @SuppressWarnings("unchecked")
- Set<Protocol> protocols = (Set<Protocol>) portAttributes.get(Port.PROTOCOLS);
if (protocols == null || protocols.isEmpty())
{
// defaulting to AMQP if protocol is not specified
@@ -208,7 +135,7 @@ public class PortFactory
if (protocolTypes.size() > 1)
{
throw new IllegalConfigurationException("Found different protocol types '" + protocolTypes
- + "' on port configuration: " + portAttributes);
+ + "' for port configuration: " + portAttributes);
}
return protocolTypes.contains(ProtocolType.AMQP);
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
index 1d50be279f..cc323694e0 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java
@@ -21,6 +21,7 @@
package org.apache.qpid.server.model.adapter;
import java.io.File;
+import java.lang.reflect.Type;
import java.security.AccessControlException;
import java.security.Principal;
import java.util.ArrayList;
@@ -38,6 +39,7 @@ import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.SystemConfiguration;
+import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
@@ -50,6 +52,7 @@ import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.Connection;
import org.apache.qpid.server.model.Exchange;
+import org.apache.qpid.server.model.IntegrityViolationException;
import org.apache.qpid.server.model.LifetimePolicy;
import org.apache.qpid.server.model.Port;
import org.apache.qpid.server.model.Protocol;
@@ -76,14 +79,17 @@ import org.apache.qpid.server.txn.ServerTransaction;
import org.apache.qpid.server.util.MapValueConverter;
import org.apache.qpid.server.virtualhost.VirtualHostImpl;
import org.apache.qpid.server.virtualhost.VirtualHostRegistry;
+import org.apache.qpid.util.FileUtils;
public final class VirtualHostAdapter extends AbstractAdapter implements VirtualHost, ExchangeRegistry.RegistryChangeListener,
QueueRegistry.RegistryChangeListener,
IConnectionRegistry.RegistryChangeListener
{
+ private static final Logger LOGGER = Logger.getLogger(VirtualHostAdapter.class);
+
@SuppressWarnings("serial")
- public static final Map<String, Class<?>> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Class<?>>(){{
+ public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{
put(NAME, String.class);
put(STORE_PATH, String.class);
put(STORE_TYPE, String.class);
@@ -124,26 +130,25 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
}
String configurationFile = (String) getAttribute(CONFIG_PATH);
- String storePath = (String) getAttribute(STORE_PATH);
String storeType = (String) getAttribute(STORE_TYPE);
boolean invalidAttributes = false;
if (configurationFile == null)
{
- if (storePath == null || storeType == null)
+ if (storeType == null)
{
invalidAttributes = true;
}
}
else
{
- if (storePath != null || storeType != null)
+ if (storeType != null)
{
invalidAttributes = true;
}
}
if (invalidAttributes)
{
- throw new IllegalConfigurationException("Please specify either the 'configPath' attribute or both 'storePath' and 'storeType' attributes");
+ throw new IllegalConfigurationException("Please specify either the 'configPath' attribute or 'storeType' and 'storePath' attributes");
}
}
@@ -963,10 +968,19 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
else if (desiredState == State.DELETED)
{
//TODO: add ACL check to authorize the operation
+
+ String hostName = getName();
+
+ if (hostName.equals(_broker.getAttribute(Broker.DEFAULT_VIRTUAL_HOST)))
+ {
+ throw new IntegrityViolationException("Cannot delete default virtual host '" + hostName + "'");
+ }
if (_virtualHost != null && _virtualHost.getState() == org.apache.qpid.server.virtualhost.State.ACTIVE)
{
setDesiredState(currentState, State.STOPPED);
}
+ _virtualHost = null;
+ setAttribute(VirtualHost.STATE, getActualState(), State.DELETED);
return true;
}
return false;
@@ -1043,4 +1057,166 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual
return _virtualHost.getMessageStore();
}
+ @Override
+ protected void changeAttributes(Map<String, Object> attributes)
+ {
+ if (State.ACTIVE.equals(getActualState()))
+ {
+ throw new IllegalStateException("Cannot change host attributes on active virtual host. This operation is only supported in management mode.");
+ }
+ Map<String, Object> newAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES);
+ validateConfigurationAndCopyStoreIntoNewLocationIfRequired(newAttributes);
+ super.changeAttributes(newAttributes);
+ }
+
+ private void validateConfigurationAndCopyStoreIntoNewLocationIfRequired(Map<String, Object> newAttributes)
+ {
+ String name = (String)getAttribute(NAME);
+ String configPath = (String)getAttribute(CONFIG_PATH);
+ String storePath = (String)getAttribute(STORE_PATH);
+ String storeType = (String)getAttribute(STORE_TYPE);
+
+ String newConfigPath = (String)newAttributes.get(CONFIG_PATH);
+ String newStorePath = (String)newAttributes.get(STORE_PATH);
+ String newStoreType = (String)newAttributes.get(STORE_TYPE);
+
+ String newName = (String)newAttributes.get(NAME);
+ if (newName != null && !newName.equals(name))
+ {
+ if (name.equals(_broker.getAttribute(Broker.DEFAULT_VIRTUAL_HOST)))
+ {
+ throw new IntegrityViolationException("Cannot rename virtual host '" + name + "' as it is set as a default." +
+ " Change the broker default virtual host before renaming");
+ }
+ }
+ if (newConfigPath != null)
+ {
+ // try to open new configuration xml and extract information about message store
+ try
+ {
+ Map<String, String> storeDetails = getStoreDetailsFromVirtualHostConfigXml(name, configPath);
+ newStorePath = storeDetails.get(STORE_PATH);
+ newStoreType = storeDetails.get(STORE_TYPE);
+ }
+ catch (Exception e)
+ {
+ throw new IllegalConfigurationException("Cannot open new virtual host configuration at " + newConfigPath, e);
+ }
+ newAttributes.put(STORE_PATH, null);
+ newAttributes.put(STORE_TYPE, null);
+ }
+ else
+ {
+ newAttributes.put(CONFIG_PATH, null);
+ }
+
+ if (configPath != null )
+ {
+ // try to identify store type and location in order to copy old store into a new location
+ try
+ {
+ Map<String, String> storeDetails = getStoreDetailsFromVirtualHostConfigXml(name, configPath);
+ storePath = storeDetails.get(STORE_PATH);
+ storeType = storeDetails.get(STORE_TYPE);
+ }
+ catch (Exception e)
+ {
+ // old configuration might be broken
+ LOGGER.warn("Cannot open virtual host cofiguration at " + configPath + ". Ignoring old broken configuration.", e);
+ }
+ }
+
+ if (storeType != null && storePath != null && newStoreType != null)
+ {
+ File oldStoreLocation = new File(storePath);
+ if (oldStoreLocation.exists())
+ {
+ if (newStoreType.equals(newStoreType))
+ {
+ File newStoreLocation = new File(newStorePath);
+ if (!oldStoreLocation.equals(newStoreLocation))
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Copying store for virtual host '" + name + "' from '"
+ + oldStoreLocation.getAbsolutePath() + "' into '" + newStoreLocation.getAbsolutePath() + "'");
+ }
+ copyStoreFiles(oldStoreLocation, newStoreLocation);
+ }
+ }
+ else
+ {
+ LOGGER.warn("Requested a message store of different type ("
+ + newStoreType + ") than existing store (" + storeType
+ + "). At the moment, copying of data is not supported for stores of different types."
+ + " As result an empty new store will be created and old data will be lost.");
+ }
+ }
+ else
+ {
+ if (LOGGER.isInfoEnabled())
+ {
+ LOGGER.info("Virtual host '" + name + "' store does not exists at " + oldStoreLocation.getAbsolutePath() + ". Skipping srore copying...");
+ }
+ }
+ }
+ }
+
+ private void copyStoreFiles(File oldStoreLocation, File newStoreLocation)
+ {
+ if (!newStoreLocation.exists() && !newStoreLocation.getParentFile().exists())
+ {
+ newStoreLocation.getParentFile().mkdirs();
+ }
+ try
+ {
+ if (oldStoreLocation.isFile())
+ {
+ if (!newStoreLocation.exists())
+ {
+ newStoreLocation.createNewFile();
+ }
+ FileUtils.copy(oldStoreLocation, newStoreLocation);
+ }
+ else
+ {
+ if (!newStoreLocation.exists())
+ {
+ newStoreLocation.mkdir();
+ }
+ FileUtils.copyRecursive(oldStoreLocation, newStoreLocation);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new IllegalConfigurationException("Cannot copy store data into a new location at " + newStoreLocation, e);
+ }
+ }
+
+ private Map<String, String> getStoreDetailsFromVirtualHostConfigXml(String name, String configPath) throws Exception
+ {
+ Map<String, String> storeDetails = new HashMap<String, String>();
+ VirtualHostConfiguration configuration = new VirtualHostConfiguration(name, new File(configPath) , _broker);
+ String storePath = configuration.getStoreConfiguration().getString("environment-path");
+ String storeType = configuration.getStoreConfiguration().getString("type");
+ if (storeType == null)
+ {
+ String storeClass = configuration.getStoreConfiguration().getString("class");
+ if (storeClass != null)
+ {
+ final Class<?> clazz = Class.forName(storeClass);
+ final Object o = clazz.newInstance();
+
+ if (o instanceof MessageStore)
+ {
+ MessageStore ms = (MessageStore)o;
+ storeType = ms.getStoreType();
+ }
+ }
+ }
+
+ storeDetails.put(STORE_PATH, storePath);
+ storeDetails.put(STORE_TYPE, storeType);
+ return storeDetails;
+ }
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java
index aa7b4afcae..8c57d04348 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java
@@ -20,7 +20,9 @@
*/
package org.apache.qpid.server.util;
-import java.util.Collection;
+import java.lang.reflect.Array;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -264,93 +266,57 @@ public class MapValueConverter
}
}
- @SuppressWarnings("unchecked")
public static <T extends Enum<T>> Set<T> getEnumSetAttribute(String name, Map<String, Object> attributes, Class<T> clazz)
{
Object obj = attributes.get(name);
- Object[] items = null;
if (obj == null)
{
return null;
}
- else if (obj instanceof Collection)
- {
- Collection<?> data = (Collection<?>) obj;
- items = data.toArray(new Object[data.size()]);
- }
- else if (obj instanceof String[])
- {
- items = (String[]) obj;
- }
- else if (obj instanceof Object[])
- {
- items = (Object[]) obj;
- }
else
{
- throw new IllegalArgumentException("Value for attribute " + name + "[" + obj
- + "] cannot be converted into set of enum of " + clazz);
- }
- Set<T> set = new HashSet<T>();
- for (int i = 0; i < items.length; i++)
- {
- T item = null;
- Object value = items[i];
- if (value instanceof String)
- {
- item = (T) Enum.valueOf(clazz, (String) value);
- }
- else if (clazz.isInstance(value))
- {
- item = (T) value;
- }
- else
- {
- throw new IllegalArgumentException("Cannot convert " + value + " from [" + obj + "] into enum of " + clazz
- + " for attribute " + name);
- }
- set.add(item);
+ return toSet(obj, clazz, name);
}
- return set;
}
- @SuppressWarnings("unchecked")
- public static Map<String, Object> convert(Map<String, Object> configurationAttributes, Map<String, Class<?>> attributeTypes)
+ public static Map<String, Object> convert(Map<String, Object> configurationAttributes, Map<String, Type> attributeTypes)
{
Map<String, Object> attributes = new HashMap<String, Object>();
- for (Map.Entry<String, Class<?>> attributeEntry : attributeTypes.entrySet())
+ for (Map.Entry<String, Type> attributeEntry : attributeTypes.entrySet())
{
String attributeName = attributeEntry.getKey();
if (configurationAttributes.containsKey(attributeName))
{
- Class<?> classObject = attributeEntry.getValue();
+ Type typeObject = attributeEntry.getValue();
Object rawValue = configurationAttributes.get(attributeName);
Object value = null;
- if (classObject == Long.class || classObject == long.class)
- {
- value = toLong(attributeName, rawValue);
- }
- else if (classObject == Integer.class || classObject == int.class)
- {
- value = toInteger(attributeName, rawValue);
- }
- else if (classObject == Boolean.class || classObject == boolean.class)
+ if (typeObject instanceof Class)
{
- value = toBoolean(attributeName, rawValue);
+ Class<?> classObject = (Class<?>)typeObject;
+ value = convert(rawValue, classObject, attributeName);
}
- else if (classObject == String.class)
+ else if (typeObject instanceof ParameterizedType)
{
- value = toString(rawValue);
- }
- else if (Enum.class.isAssignableFrom(classObject))
- {
- @SuppressWarnings("rawtypes")
- Class<Enum> enumType = (Class<Enum>)classObject;
- value = toEnum(attributeName, rawValue, enumType);
+ ParameterizedType parameterizedType= (ParameterizedType)typeObject;
+ Type type = parameterizedType.getRawType();
+ if (type == Set.class)
+ {
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+ if (actualTypeArguments.length != 1)
+ {
+ throw new IllegalArgumentException("Set type argument is not specified");
+ }
+ Class<?> classObject = (Class<?>)actualTypeArguments[0];
+ value = toSet(rawValue, classObject, attributeName);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Convertion into " + parameterizedType + " is not yet supported");
+ }
}
else
{
- throw new IllegalArgumentException("Cannot convert '" + rawValue + "' into " + classObject);
+ throw new IllegalArgumentException("Convertion into " + typeObject + " is not yet supported");
}
attributes.put(attributeName, value);
}
@@ -358,4 +324,65 @@ public class MapValueConverter
return attributes;
}
+ public static <T> Set<T> toSet(Object rawValue, Class<T> setItemClass, String attributeName)
+ {
+ HashSet<T> set = new HashSet<T>();
+ if (rawValue instanceof Iterable)
+ {
+ Iterable<?> iterable = (Iterable<?>)rawValue;
+ for (Object object : iterable)
+ {
+ T converted = convert(object, setItemClass, attributeName);
+ set.add(converted);
+ }
+ }
+ else if (rawValue.getClass().isArray())
+ {
+ int length = Array.getLength(rawValue);
+ for (int i = 0; i < length; i ++)
+ {
+ Object arrayElement = Array.get(rawValue, i);
+ T converted = convert(arrayElement, setItemClass, attributeName);
+ set.add(converted);
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("Cannot convert '" + rawValue.getClass() + "' into Set<" + setItemClass.getSimpleName() + "> for attribute " + attributeName);
+ }
+ return set;
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public static <T> T convert(Object rawValue, Class<T> classObject, String attributeName)
+ {
+ Object value;
+ if (classObject == Long.class || classObject == long.class)
+ {
+ value = toLong(attributeName, rawValue);
+ }
+ else if (classObject == Integer.class || classObject == int.class)
+ {
+ value = toInteger(attributeName, rawValue);
+ }
+ else if (classObject == Boolean.class || classObject == boolean.class)
+ {
+ value = toBoolean(attributeName, rawValue);
+ }
+ else if (classObject == String.class)
+ {
+ value = toString(rawValue);
+ }
+ else if (Enum.class.isAssignableFrom(classObject))
+ {
+ value = toEnum(attributeName, rawValue, (Class<Enum>) classObject);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Cannot convert '" + rawValue + "' of type '" + rawValue.getClass()
+ + "' into type " + classObject + " for attribute " + attributeName);
+ }
+ return (T) value;
+ }
+
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/ParameterizedTypeImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/ParameterizedTypeImpl.java
new file mode 100644
index 0000000000..29bc81caab
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/ParameterizedTypeImpl.java
@@ -0,0 +1,73 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+public class ParameterizedTypeImpl implements ParameterizedType
+{
+ private Class<?> _rawType;
+ private Type[] _typeArguments;
+
+ public ParameterizedTypeImpl(Class<?> rawType, Class<?>... typeArguments)
+ {
+ _rawType = rawType;
+ _typeArguments = typeArguments;
+ }
+ @Override
+ public Type[] getActualTypeArguments()
+ {
+ return _typeArguments;
+ }
+
+ @Override
+ public Type getRawType()
+ {
+ return _rawType;
+ }
+
+ @Override
+ public Type getOwnerType()
+ {
+ return _rawType.getDeclaringClass();
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(_rawType.getName());
+ if (_typeArguments != null)
+ {
+ sb.append("<");
+ for (int i = 0; i < _typeArguments.length; i++)
+ {
+ sb.append(_typeArguments[i].getClass().getName());
+ if (i < _typeArguments.length - 1)
+ {
+ sb.append(",");
+ }
+ }
+ sb.append(">");
+ }
+ return sb.toString();
+ }
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java
index 57d219f85f..eb5b2b350f 100644
--- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/VirtualHostRecovererTest.java
@@ -91,7 +91,7 @@ public class VirtualHostRecovererTest extends TestCase
attributes.put(VirtualHost.NAME, getName());
attributes.put(VirtualHost.STORE_PATH, "/path/to/store");
attributes.put(VirtualHost.STORE_TYPE, "DERBY");
- mandatoryAttributes = new String[]{VirtualHost.NAME, VirtualHost.STORE_PATH, VirtualHost.STORE_TYPE};
+ mandatoryAttributes = new String[]{VirtualHost.NAME, VirtualHost.STORE_TYPE};
checkMandatoryAttributesAreValidated(mandatoryAttributes, attributes);
}