diff options
Diffstat (limited to 'qpid/java')
24 files changed, 1054 insertions, 225 deletions
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java index c2ac675e20..8fa9a59038 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.management.plugin; import java.io.File; +import java.lang.reflect.Type; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -120,7 +121,7 @@ public class HttpManagement extends AbstractPluginAdapter }}); @SuppressWarnings("serial") - private static final Map<String, Class<?>> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Class<?>>(){{ + private static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{ put(HTTP_BASIC_AUTHENTICATION_ENABLED, Boolean.class); put(HTTPS_BASIC_AUTHENTICATION_ENABLED, Boolean.class); put(HTTP_SASL_AUTHENTICATION_ENABLED, Boolean.class); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java index 3fab26cde5..8b74eb1dce 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java @@ -348,16 +348,7 @@ public class RestServlet extends AbstractServlet Collection<ConfiguredObject>[] objects = new Collection[_hierarchy.length]; if(_hierarchy.length == 1) { - try - { - getBroker().createChild(_hierarchy[0], providedObject); - } - catch (RuntimeException e) - { - setResponseStatus(response, e); - return; - } - + createOrUpdate(providedObject, _hierarchy[0], getBroker(), null, response); } else { @@ -419,40 +410,39 @@ public class RestServlet extends AbstractServlet ConfiguredObject theParent = parents.remove(0); ConfiguredObject[] otherParents = parents.toArray(new ConfiguredObject[parents.size()]); - try - { + createOrUpdate(providedObject, objClass, theParent, otherParents, response); + } + } - Collection<? extends ConfiguredObject> existingChildren = theParent.getChildren(objClass); - for(ConfiguredObject obj: existingChildren) + private void createOrUpdate(Map<String, Object> providedObject, Class<? extends ConfiguredObject> objClass, + ConfiguredObject theParent, ConfiguredObject[] otherParents, HttpServletResponse response) throws IOException + { + try + { + Collection<? extends ConfiguredObject> existingChildren = theParent.getChildren(objClass); + for(ConfiguredObject obj: existingChildren) + { + if((providedObject.containsKey("id") && String.valueOf(providedObject.get("id")).equals(obj.getId().toString())) + || (obj.getName().equals(providedObject.get("name")) && equalParents(obj, otherParents))) { - if((providedObject.containsKey("id") && String.valueOf(providedObject.get("id")).equals(obj.getId().toString())) - || (obj.getName().equals(providedObject.get("name")) && equalParents(obj, otherParents))) - { - doUpdate(obj, providedObject); - response.setStatus(HttpServletResponse.SC_OK); - return; - } + doUpdate(obj, providedObject); + response.setStatus(HttpServletResponse.SC_OK); + return; } - - theParent.createChild(objClass, providedObject, otherParents); - } - catch (RuntimeException e) - { - setResponseStatus(response, e); - return; } + theParent.createChild(objClass, providedObject, otherParents); + response.setStatus(HttpServletResponse.SC_CREATED); + } + catch (RuntimeException e) + { + setResponseStatus(response, e); } - response.setStatus(HttpServletResponse.SC_CREATED); } private void doUpdate(ConfiguredObject obj, Map<String, Object> providedObject) { - for(Map.Entry<String,Object> entry : providedObject.entrySet()) - { - obj.setAttribute(entry.getKey(), obj.getAttribute(entry.getKey()), entry.getValue()); - } - //TODO - Implement. + obj.setAttributes(providedObject); } private boolean equalParents(ConfiguredObject obj, ConfiguredObject[] otherParents) diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js index 7613fd5d71..4273ed5b41 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js @@ -72,6 +72,7 @@ define(["dojo/_base/xhr", { this.controller = controller; this.name = query(".name", node)[0]; + this.type = query(".type", node)[0]; /*this.state = dom.byId("state"); this.durable = dom.byId("durable"); this.lifetimePolicy = dom.byId("lifetimePolicy"); @@ -89,7 +90,7 @@ define(["dojo/_base/xhr", that.updateHeader(); - require(["qpid/management/authenticationprovider/"+that.authProviderData.type], + require(["qpid/management/authenticationprovider/"+that.authProviderData.category], function(SpecificProvider) { that.details = new SpecificProvider(node, authProviderObj, controller); that.details.update(); @@ -102,6 +103,7 @@ define(["dojo/_base/xhr", AuthProviderUpdater.prototype.updateHeader = function() { this.name.innerHTML = this.authProviderData[ "name" ]; + this.type.innerHTML = this.authProviderData[ "authenticationProviderType" ]; /* this.state.innerHTML = this.brokerData[ "state" ]; this.durable.innerHTML = this.brokerData[ "durable" ]; this.lifetimePolicy.innerHTML = this.brokerData[ "lifetimePolicy" ]; diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java index 8f087ba50c..f53b369221 100644 --- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java +++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagement.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.jmx; import java.io.IOException; +import java.lang.reflect.Type; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -82,7 +83,7 @@ public class JMXManagement extends AbstractPluginAdapter implements Configuratio }}; @SuppressWarnings("serial") - private static final Map<String, Class<?>> ATTRIBUTE_TYPES = new HashMap<String, Class<?>>(){{ + private static final Map<String, Type> ATTRIBUTE_TYPES = new HashMap<String, Type>(){{ put(USE_PLATFORM_MBEAN_SERVER, Boolean.class); put(NAME, String.class); put(PluginFactory.PLUGIN_TYPE, String.class); 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); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java index a171b4459b..157945f2be 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AuthenticationProviderRestTest.java @@ -20,17 +20,26 @@ */ package org.apache.qpid.systest.rest; +import java.io.File; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.User; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManagerFactory; +import org.apache.qpid.server.security.auth.manager.PlainPasswordFileAuthenticationManagerFactory; +import org.apache.qpid.test.utils.TestBrokerConfiguration; public class AuthenticationProviderRestTest extends QpidRestTestCase { + private static final String PRINCIPAL_DATABASE_AUTHENTICATION_MANAGER = "PrincipalDatabaseAuthenticationManager"; + private static final String AUTHENTICATION_MANAGER = "AuthenticationManager"; + public void testGet() throws Exception { List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider"); @@ -38,15 +47,195 @@ public class AuthenticationProviderRestTest extends QpidRestTestCase assertEquals("Unexpected number of providers", 1, providerDetails.size()); for (Map<String, Object> provider : providerDetails) { - assertProvider("PrincipalDatabaseAuthenticationManager", provider); + assertProvider(PRINCIPAL_DATABASE_AUTHENTICATION_MANAGER, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE, provider); Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/authenticationprovider/" + provider.get(AuthenticationProvider.NAME)); assertNotNull("Cannot load data for " + provider.get(AuthenticationProvider.NAME), data); - assertProvider("PrincipalDatabaseAuthenticationManager", data); + assertProvider(PRINCIPAL_DATABASE_AUTHENTICATION_MANAGER, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE, data); + } + } + + public void testPutCreateNewPlainPrincipalDatabaseProvider() throws Exception + { + File principalDatabase = getRestTestHelper().createTemporaryPasswdFile(new String[]{"admin2", "guest2", "test2"}); + + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); + attributes.put(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH, principalDatabase.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + Map<String, Object> provider = providerDetails.get(0); + assertProvider(PRINCIPAL_DATABASE_AUTHENTICATION_MANAGER, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE, provider); + + // provider should exists after broker restart + restartBroker(); + providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + } + + public void testPutCreateNewAnonymousProvider() throws Exception + { + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + Map<String, Object> provider = providerDetails.get(0); + assertProvider(AUTHENTICATION_MANAGER, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE, provider); + } + + public void testDeleteOfDefaultAuthenticationProviderFails() throws Exception + { + String providerName = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER; + + int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName , "DELETE", null); + assertEquals("Unexpected response code", 409, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + assertProvider(PRINCIPAL_DATABASE_AUTHENTICATION_MANAGER, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE, providerDetails.get(0)); + } + + public void testDeleteOfUsedAuthenticationProviderFails() throws Exception + { + // create provider + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code for provider creation", 201, responseCode); + + // create port + String portName = "test-port"; + Map<String, Object> portAttributes = new HashMap<String, Object>(); + portAttributes.put(Port.NAME, portName); + portAttributes.put(Port.AUTHENTICATION_MANAGER, providerName); + portAttributes.put(Port.PORT, findFreePort()); + + responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", portAttributes); + assertEquals("Unexpected response code for port creation", 201, responseCode); + + responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName , "DELETE", null); + assertEquals("Unexpected response code for provider deletion", 409, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + assertProvider(AUTHENTICATION_MANAGER, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE, providerDetails.get(0)); + } + + public void testDeleteOfUnusedAuthenticationProvider() throws Exception + { + // create provider + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code for provider creation", 201, responseCode); + + responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName , "DELETE", null); + assertEquals("Unexpected response code for provider deletion", 200, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 0, providerDetails.size()); + } + + public void testPutUpdateAuthenticationProvider() throws Exception + { + File principalDatabase = getRestTestHelper().createTemporaryPasswdFile(new String[]{"admin2", "guest2", "test2"}); + + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); + attributes.put(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH, principalDatabase.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + + String[] users = new String[]{"admin3", "guest3", "test3"}; + File principalDatabase2 = getRestTestHelper().createTemporaryPasswdFile(users); + + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(AuthenticationProvider.NAME, providerName); + newAttributes.put(AuthenticationProvider.TYPE, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); + newAttributes.put(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH, principalDatabase2.getAbsolutePath()); + + responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", newAttributes); + assertEquals("Unexpected response code", 200, responseCode); + + providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + + List<Map<String, Object>> userData = getRestTestHelper().getJsonAsList("/rest/user/" + providerName); + assertEquals(3, userData.size()); + for (int i = 0; i < users.length; i++) + { + boolean userFound = false; + for (Map<String, Object> userEntry : userData) + { + String name = (String)userEntry.get(User.NAME); + if (users[i].equals(name)) + { + userFound = true; + break; + } + } + assertTrue("User " + users[i] + " is not found", userFound); } } - private void assertProvider(String type, Map<String, Object> provider) + public void testPutUpdateAuthenticationProviderToDifferrentCategoryFails() throws Exception + { + File principalDatabase = getRestTestHelper().createTemporaryPasswdFile(new String[]{"admin2", "guest2", "test2"}); + + String providerName = "test-provider"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AuthenticationProvider.NAME, providerName); + attributes.put(AuthenticationProvider.TYPE, PlainPasswordFileAuthenticationManagerFactory.PROVIDER_TYPE); + attributes.put(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH, principalDatabase.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider/" + providerName); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(AuthenticationProvider.NAME, providerName); + newAttributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE); + + responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + providerName, "PUT", newAttributes); + assertEquals("Unexpected response code", 409, responseCode); + } + + private void assertProvider(String category, String subType, Map<String, Object> provider) { Asserts.assertAttributesPresent(provider, AuthenticationProvider.AVAILABLE_ATTRIBUTES, AuthenticationProvider.CREATED, AuthenticationProvider.UPDATED, AuthenticationProvider.DESCRIPTION, @@ -57,17 +246,22 @@ public class AuthenticationProviderRestTest extends QpidRestTestCase LifetimePolicy.PERMANENT.name(), provider.get(AuthenticationProvider.LIFETIME_POLICY)); assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.DURABLE, Boolean.TRUE, provider.get(AuthenticationProvider.DURABLE)); - assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.TYPE, type, + assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.CATEGORY, category, + provider.get(AuthenticationProvider.CATEGORY)); + assertEquals("Unexpected value of provider attribute " + AuthenticationProvider.TYPE, subType, provider.get(AuthenticationProvider.TYPE)); - @SuppressWarnings("unchecked") - List<Map<String, Object>> users = (List<Map<String, Object>>) provider.get("users"); - assertNotNull("Users are not found", users); - assertTrue("Unexpected number of users", users.size() > 1); - for (Map<String, Object> user : users) + if (PRINCIPAL_DATABASE_AUTHENTICATION_MANAGER.equals(category)) { - assertNotNull("Attribute " + User.ID, user.get(User.ID)); - assertNotNull("Attribute " + User.NAME, user.get(User.NAME)); + @SuppressWarnings("unchecked") + List<Map<String, Object>> users = (List<Map<String, Object>>) provider.get("users"); + assertNotNull("Users are not found", users); + assertTrue("Unexpected number of users", users.size() > 1); + for (Map<String, Object> user : users) + { + assertNotNull("Attribute " + User.ID, user.get(User.ID)); + assertNotNull("Attribute " + User.NAME, user.get(User.NAME)); + } } } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java index 578565be05..48fd8f44b1 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java @@ -21,10 +21,17 @@ package org.apache.qpid.systest.rest; import java.net.URLDecoder; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.plugin.AuthenticationManagerFactory; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManagerFactory; import org.apache.qpid.test.utils.TestBrokerConfiguration; public class PortRestTest extends QpidRestTestCase @@ -61,4 +68,111 @@ public class PortRestTest extends QpidRestTestCase } } + public void testPutAmqpPortWithMinimumAttributes() throws Exception + { + String portName = "test-port"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.PORT, findFreePort()); + + int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + List<Map<String, Object>> portDetails = getRestTestHelper().getJsonAsList("/rest/port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + Map<String, Object> port = portDetails.get(0); + Asserts.assertPortAttributes(port); + + // make sure that port is there after broker restart + restartBroker(); + + portDetails = getRestTestHelper().getJsonAsList("/rest/port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + } + + public void testPutRmiPortWithMinimumAttributes() throws Exception + { + String portName = "test-port"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.PORT, findFreePort()); + attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.RMI)); + + int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); + assertEquals("Unexpected response code", 201, responseCode); + + List<Map<String, Object>> portDetails = getRestTestHelper().getJsonAsList("/rest/port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + Map<String, Object> port = portDetails.get(0); + Asserts.assertPortAttributes(port); + + // make sure that port is there after broker restart + restartBroker(); + + portDetails = getRestTestHelper().getJsonAsList("/rest/port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + } + + public void testPutCreateAndUpdateAmqpPort() throws Exception + { + String portName = "test-port"; + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.NAME, portName); + attributes.put(Port.PORT, findFreePort()); + + int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); + assertEquals("Unexpected response code for port creation", 201, responseCode); + + List<Map<String, Object>> portDetails = getRestTestHelper().getJsonAsList("/rest/port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + Map<String, Object> port = portDetails.get(0); + Asserts.assertPortAttributes(port); + + Map<String, Object> authProviderAttributes = new HashMap<String, Object>(); + authProviderAttributes.put(AuthenticationManagerFactory.ATTRIBUTE_TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE); + authProviderAttributes.put(AuthenticationProvider.NAME, TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); + + responseCode = getRestTestHelper().submitRequest("/rest/authenticationprovider/" + TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER, "PUT", authProviderAttributes); + assertEquals("Unexpected response code for authentication provider creation", 201, responseCode); + + attributes = new HashMap<String, Object>(port); + attributes.put(Port.AUTHENTICATION_MANAGER, TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); + attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.AMQP_0_9_1)); + + responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); + assertEquals("Unexpected response code for port update", 200, responseCode); + + portDetails = getRestTestHelper().getJsonAsList("/rest/port/" + portName); + assertNotNull("Port details cannot be null", portDetails); + assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); + port = portDetails.get(0); + + assertEquals("Unexpected authentication provider", TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER, port.get(Port.AUTHENTICATION_MANAGER)); + Object protocols = port.get(Port.PROTOCOLS); + assertNotNull("Protocols attribute is not found", protocols); + assertTrue("Protocol attribute value is not collection:" + protocols, protocols instanceof Collection); + @SuppressWarnings("unchecked") + Collection<String> protocolsCollection = ((Collection<String>)protocols); + assertEquals("Unexpected protocols size", 1, protocolsCollection.size()); + assertEquals("Unexpected protocols", Protocol.AMQP_0_9_1.name(), protocolsCollection.iterator().next()); + } + + public void testPutUpdateOpenedAmqpPortFails() throws Exception + { + Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT); + Integer portValue = (Integer)port.get(Port.PORT); + + port.put(Port.PORT, findFreePort()); + + int responseCode = getRestTestHelper().submitRequest("/rest/port/" + TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT, "PUT", port); + assertEquals("Unexpected response code for port update", 409, responseCode); + + port = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT); + assertEquals("Port has been changed", portValue, port.get(Port.PORT)); + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java index 0db1f7e50d..9628423a00 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java @@ -422,7 +422,7 @@ public class RestTestHelper } } - private File createTemporaryPasswdFile(String[] users) throws IOException + File createTemporaryPasswdFile(String[] users) throws IOException { BufferedWriter writer = null; try @@ -449,4 +449,17 @@ public class RestTestHelper } } } + + public int submitRequest(String url, String method, Map<String, Object> attributes) throws IOException, + JsonGenerationException, JsonMappingException + { + HttpURLConnection connection = openManagementConnection(url, method); + if (attributes != null) + { + writeJsonRequest(connection, attributes); + } + int responseCode = connection.getResponseCode(); + connection.disconnect(); + return responseCode; + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java index fb2c941203..c5dfb84d40 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/VirtualHostRestTest.java @@ -169,6 +169,112 @@ public class VirtualHostRestTest extends QpidRestTestCase assertEquals("Host should be deleted", 0, hosts.size()); } + public void testDeleteDefaultHostFails() throws Exception + { + String hostToDelete = TEST1_VIRTUALHOST; + int response = getRestTestHelper().submitRequest("/rest/virtualhost/" + hostToDelete, "DELETE", null); + assertEquals("Unexpected response code", 409, response); + + restartBroker(); + + List<Map<String, Object>> hosts = getRestTestHelper().getJsonAsList("/rest/virtualhost/" + hostToDelete); + assertEquals("Host should be deleted", 1, hosts.size()); + } + + public void testUpdateActiveHostFails() throws Exception + { + String hostToUpdate = TEST3_VIRTUALHOST; + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/" + hostToUpdate); + Asserts.assertVirtualHost(hostToUpdate, hostDetails); + String configPath = (String)hostDetails.get(VirtualHost.CONFIG_PATH); + assertNotNull("Unexpected host configuration", configPath); + + String storeType = getTestProfileMessageStoreType(); + String storeLocation = getStoreLocation(hostToUpdate); + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(VirtualHost.NAME, hostToUpdate); + newAttributes.put(VirtualHost.STORE_TYPE, storeType); + newAttributes.put(VirtualHost.STORE_PATH, storeLocation); + + int response = getRestTestHelper().submitRequest("/rest/virtualhost/" + hostToUpdate, "PUT", newAttributes); + assertEquals("Unexpected response code", 409, response); + + restartBroker(); + + hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/" + hostToUpdate); + Asserts.assertVirtualHost(hostToUpdate, hostDetails); + assertEquals("Unexpected config path", configPath, hostDetails.get(VirtualHost.CONFIG_PATH)); + } + + public void testUpdateInManagementMode() throws Exception + { + String hostToUpdate = TEST3_VIRTUALHOST; + Map<String, Object> hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/" + hostToUpdate); + Asserts.assertVirtualHost(hostToUpdate, hostDetails); + String configPath = (String)hostDetails.get(VirtualHost.CONFIG_PATH); + String storeType = (String)hostDetails.get(VirtualHost.STORE_TYPE); + assertNotNull("Unexpected host configuration", configPath); + assertNotNull("Unexpected store type", storeType); + + String queueName = getTestQueueName(); + if (isBrokerStorePersistent()) + { + String storePath = (String)hostDetails.get(VirtualHost.STORE_PATH); + assertNotNull("Unexpected store path", storePath); + + // add a durable queue to the host + // in order to test whether host store is copied into a new location + Map<String, Object> queueData = new HashMap<String, Object>(); + queueData.put(Queue.NAME, queueName); + queueData.put(Queue.DURABLE, Boolean.TRUE); + int response = getRestTestHelper().submitRequest("/rest/queue/" + hostToUpdate + "/" + queueName, "PUT", queueData); + assertEquals("Unexpected response code for queue creation", 201, response); + } + + // stop broker as it is running not in management mode + stopBroker(0); + + // start broker in management mode + startBroker(0, true); + + String newStoreType = getTestProfileMessageStoreType(); + String newStorePath = getStoreLocation(hostToUpdate); + Map<String, Object> newAttributes = new HashMap<String, Object>(); + newAttributes.put(VirtualHost.NAME, hostToUpdate); + newAttributes.put(VirtualHost.STORE_TYPE, newStoreType); + newAttributes.put(VirtualHost.STORE_PATH, newStorePath); + newAttributes.put(VirtualHost.CONFIG_PATH, null); + + try + { + int response = getRestTestHelper().submitRequest("/rest/virtualhost/" + hostToUpdate, "PUT", newAttributes); + assertEquals("Unexpected response code for virtual host update", 200, response); + + restartBroker(); + + hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/" + hostToUpdate); + Asserts.assertVirtualHost(hostToUpdate, hostDetails); + assertEquals("Unexpected config type", newStoreType, hostDetails.get(VirtualHost.STORE_TYPE)); + + if (isBrokerStorePersistent()) + { + assertEquals("Unexpected config path", newStorePath, hostDetails.get(VirtualHost.STORE_PATH)); + + // the virtual host store should be copied into a new location + // check existence of the queue + List<Map<String, Object>> queues = getRestTestHelper().getJsonAsList("/rest/queue/" + hostToUpdate + "/" + queueName); + assertEquals("Queue is not found. Looks like message store was not copied", 1, queues.size()); + } + } + finally + { + if (newStorePath != null) + { + FileUtils.delete(new File(newStorePath), true); + } + } + } + public void testPutCreateQueue() throws Exception { String queueName = getTestQueueName(); @@ -519,7 +625,6 @@ public class VirtualHostRestTest extends QpidRestTestCase private int tryCreateVirtualHost(String hostName, String storeType, String storePath, String configPath) throws IOException, JsonGenerationException, JsonMappingException { - HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/virtualhost/" + hostName, "PUT"); Map<String, Object> hostData = new HashMap<String, Object>(); hostData.put(VirtualHost.NAME, hostName); @@ -533,10 +638,7 @@ public class VirtualHostRestTest extends QpidRestTestCase hostData.put(VirtualHost.STORE_TYPE, storeType); } - getRestTestHelper().writeJsonRequest(connection, hostData); - int responseCode = connection.getResponseCode(); - connection.disconnect(); - return responseCode; + return getRestTestHelper().submitRequest("/rest/virtualhost/" + hostName, "PUT", hostData); } private XMLConfiguration createAndSaveVirtualHostConfiguration(String hostName, File configFile, String storeLocation) diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java index d36f57171f..394fed7d47 100755 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java @@ -418,12 +418,16 @@ public class QpidBrokerTestCase extends QpidTestCase public void startBroker(int port) throws Exception { + startBroker(port, false); + } + + public void startBroker(int port, boolean managementMode) throws Exception + { int actualPort = getPort(port); TestBrokerConfiguration configuration = getBrokerConfiguration(actualPort); - startBroker(actualPort, configuration, _testVirtualhosts); + startBroker(actualPort, configuration, _testVirtualhosts, managementMode); } - protected File getBrokerCommandLog4JFile() { return _logConfigFile; @@ -437,6 +441,11 @@ public class QpidBrokerTestCase extends QpidTestCase public void startBroker(int port, TestBrokerConfiguration testConfiguration, XMLConfiguration virtualHosts) throws Exception { + startBroker(port, testConfiguration, virtualHosts, false); + } + + public void startBroker(int port, TestBrokerConfiguration testConfiguration, XMLConfiguration virtualHosts, boolean managementMode) throws Exception + { port = getPort(port); String testConfig = saveTestConfiguration(port, testConfiguration); String virtualHostsConfig = saveTestVirtualhosts(port, virtualHosts); @@ -457,6 +466,7 @@ public class QpidBrokerTestCase extends QpidTestCase options.setConfigurationStoreType(_brokerStoreType); options.setConfigurationStoreLocation(testConfig); + options.setManagementMode(managementMode); //Set the log config file, relying on the log4j.configuration system property //set on the JVM by the JUnit runner task in module.xml. @@ -474,6 +484,13 @@ public class QpidBrokerTestCase extends QpidTestCase final String qpidWork = getQpidWork(_brokerType, port); String[] cmd = _brokerCommandHelper.getBrokerCommand(port, testConfig, _brokerStoreType, _logConfigFile); + if (managementMode) + { + String[] newCmd = new String[cmd.length + 1]; + System.arraycopy(cmd, 0, newCmd, 0, cmd.length); + newCmd[cmd.length] = "-mm"; + cmd = newCmd; + } _logger.info("Starting spawn broker using command: " + StringUtils.join(cmd, ' ')); ProcessBuilder pb = new ProcessBuilder(cmd); pb.redirectErrorStream(true); |
