diff options
| author | Alex Rudyy <orudyy@apache.org> | 2013-02-27 17:50:17 +0000 |
|---|---|---|
| committer | Alex Rudyy <orudyy@apache.org> | 2013-02-27 17:50:17 +0000 |
| commit | 119554c72479af7ecaeafd12d0f840c8fb50f415 (patch) | |
| tree | cd59d2081f84dc36158986d573225554c273d19c /qpid/java/broker | |
| parent | 466d209aea03a17c508b887682ab769ba72f9e44 (diff) | |
| download | qpid-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')
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); } |
