You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2018/12/18 18:23:52 UTC

[qpid-broker-j] branch master updated (dc4ed69 -> f4807cb)

This is an automated email from the ASF dual-hosted git repository.

orudyy pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git.


    from dc4ed69  NO-JIRA: update pom.xml scm details to reflect new repo location
     new d0a6a70  QPID-6948: [Broker-J] Introduce interfaces for handling REST management requests and add pluggable mechanism for supporting previous versions of REST API
     new 359b9a3  QPID-6948: [Broker-J] Introduce base classes for implementation of legacy management REST API and add 7.0 REST API support
     new f4807cb  QPID-6948: [Broker-J] Add compatibility layer for REST API v6.1

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../server/management/plugin/HttpManagement.java   |   93 +-
 .../management/plugin/ManagementController.java    |   95 ++
 .../plugin/ManagementControllerFactory.java        |   47 +
 .../management/plugin/ManagementException.java     |  282 ++++++
 .../management/plugin/ManagementRequest.java       |   53 +
 .../management/plugin/ManagementResponse.java      |   34 +
 .../qpid/server/management/plugin/RequestType.java |   29 +
 .../server/management/plugin/ResponseType.java     |   28 +
 .../AbstractLegacyConfiguredObjectController.java  |  560 +++++++++++
 .../controller/AbstractManagementController.java   |  204 ++++
 .../plugin/controller/CategoryController.java      |   78 ++
 .../controller/CategoryControllerFactory.java      |   48 +
 .../controller/ControllerManagementResponse.java   |   82 ++
 .../plugin/controller/ConverterHelper.java         |  154 +++
 .../controller/GenericCategoryController.java      |  266 +++++
 .../controller/GenericLegacyConfiguredObject.java  |  195 ++++
 .../plugin/controller/LegacyConfiguredObject.java  |   90 ++
 .../LegacyConfiguredObjectToMapConverter.java      |  251 +++++
 .../controller/LegacyManagementController.java     |   44 +
 .../plugin/controller/TypeController.java          |   39 +
 .../plugin/controller/TypeControllerFactory.java   |   51 +
 .../latest/LatestManagementController.java         |  863 ++++++++++++++++
 .../latest/LatestManagementControllerAdapter.java  |  489 +++++++++
 .../latest/LatestManagementControllerFactory.java  |   56 ++
 .../v6_1/LegacyManagementController.java           |  194 ++++
 .../v6_1/LegacyManagementControllerFactory.java    |   61 ++
 .../v6_1/category/BindingController.java           |  611 +++++++++++
 .../controller/v6_1/category/BrokerController.java |  212 ++++
 .../v6_1/category/ConsumerController.java          |  257 +++++
 .../v6_1/category/DestinationController.java       |  207 ++++
 .../v6_1/category/ExchangeController.java          |   92 ++
 .../v6_1/category/LegacyCategoryController.java    |   62 ++
 .../category/LegacyCategoryControllerFactory.java  |  268 +++++
 .../controller/v6_1/category/PortController.java   |  140 +++
 .../controller/v6_1/category/QueueController.java  |  273 +++++
 .../v6_1/category/SessionController.java           |  111 ++
 .../v6_1/category/VirtualHostController.java       |  130 +++
 .../v7_0/LegacyManagementController.java           |   80 ++
 .../v7_0/LegacyManagementControllerFactory.java    |   62 ++
 .../v7_0/category/ContainerController.java         |   80 ++
 .../v7_0/category/LegacyCategoryController.java    |   63 ++
 .../category/LegacyCategoryControllerFactory.java  |  154 +++
 .../plugin/servlet/rest/RequestInfo.java           |    7 +-
 .../plugin/servlet/rest/RestServlet.java           | 1062 ++++++--------------
 .../servlet/rest/RestUserPreferenceHandler.java    |   19 +-
 ...stractLegacyConfiguredObjectControllerTest.java |  292 ++++++
 .../controller/GenericCategoryControllerTest.java  |  247 +++++
 .../GenericLegacyConfiguredObjectTest.java         |  212 ++++
 .../LatestManagementControllerAdapterTest.java     |  395 ++++++++
 .../latest/LatestManagementControllerTest.java     |  809 +++++++++++++++
 .../v6_1/LegacyManagementControllerTest.java       |  138 +++
 .../v6_1/category/BindingControllerTest.java       |  312 ++++++
 .../v6_1/category/BrokerControllerTest.java        |  104 ++
 .../v6_1/category/ConsumerControllerTest.java      |  317 ++++++
 .../v6_1/category/DestinationControllerTest.java   |  135 +++
 .../v6_1/category/ExchangeControllerTest.java      |  113 +++
 .../LegacyCategoryControllerFactoryTest.java       |   63 ++
 .../category/LegacyCategoryControllerTest.java     |   80 ++
 .../v6_1/category/PortControllerTest.java          |  113 +++
 .../v6_1/category/QueueControllerTest.java         |  161 +++
 .../v6_1/category/SessionControllerTest.java       |   95 ++
 .../v6_1/category/VirtualHostControllerTest.java   |  104 ++
 .../v7_0/LegacyManagementControllerTest.java       |  105 ++
 .../v7_0/category/ContainerControllerTest.java     |   83 ++
 .../LegacyCategoryControllerFactoryTest.java       |   58 ++
 .../plugin/servlet/rest/RequestInfoParserTest.java |   23 +-
 66 files changed, 11400 insertions(+), 835 deletions(-)
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementControllerFactory.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementException.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementRequest.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementResponse.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/RequestType.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ResponseType.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/AbstractLegacyConfiguredObjectController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/AbstractManagementController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/CategoryController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/CategoryControllerFactory.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/ControllerManagementResponse.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/ConverterHelper.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/GenericCategoryController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/GenericLegacyConfiguredObject.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyConfiguredObject.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyConfiguredObjectToMapConverter.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyManagementController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/TypeController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/TypeControllerFactory.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerAdapter.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerFactory.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementControllerFactory.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BindingController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BrokerController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ConsumerController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/DestinationController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ExchangeController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerFactory.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/PortController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/QueueController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/SessionController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/VirtualHostController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementControllerFactory.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/ContainerController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryController.java
 create mode 100644 broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryControllerFactory.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/AbstractLegacyConfiguredObjectControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/GenericCategoryControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/GenericLegacyConfiguredObjectTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerAdapterTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BindingControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BrokerControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ConsumerControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/DestinationControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ExchangeControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerFactoryTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/PortControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/QueueControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/SessionControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/VirtualHostControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/ContainerControllerTest.java
 create mode 100644 broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryControllerFactoryTest.java


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[qpid-broker-j] 02/03: QPID-6948: [Broker-J] Introduce base classes for implementation of legacy management REST API and add 7.0 REST API support

Posted by or...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit 359b9a3fc513062aed2ef0e74edd7321e43eb76b
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Tue Dec 18 17:19:59 2018 +0000

    QPID-6948: [Broker-J] Introduce base classes for implementation of legacy management REST API and add 7.0 REST API support
---
 .../AbstractLegacyConfiguredObjectController.java  | 560 +++++++++++++++++++++
 .../plugin/controller/CategoryController.java      |  78 +++
 .../controller/CategoryControllerFactory.java      |  48 ++
 .../controller/ControllerManagementResponse.java   |  82 +++
 .../controller/GenericCategoryController.java      | 266 ++++++++++
 .../controller/GenericLegacyConfiguredObject.java  | 195 +++++++
 .../plugin/controller/LegacyConfiguredObject.java  |  90 ++++
 .../LegacyConfiguredObjectToMapConverter.java      | 251 +++++++++
 .../controller/LegacyManagementController.java     |  44 ++
 .../plugin/controller/TypeController.java          |  39 ++
 .../plugin/controller/TypeControllerFactory.java   |  51 ++
 .../latest/LatestManagementControllerAdapter.java  | 489 ++++++++++++++++++
 .../v7_0/LegacyManagementController.java           |  80 +++
 .../v7_0/LegacyManagementControllerFactory.java    |  62 +++
 .../v7_0/category/ContainerController.java         |  80 +++
 .../v7_0/category/LegacyCategoryController.java    |  63 +++
 .../category/LegacyCategoryControllerFactory.java  | 154 ++++++
 ...stractLegacyConfiguredObjectControllerTest.java | 292 +++++++++++
 .../controller/GenericCategoryControllerTest.java  | 247 +++++++++
 .../GenericLegacyConfiguredObjectTest.java         | 212 ++++++++
 .../LatestManagementControllerAdapterTest.java     | 395 +++++++++++++++
 .../v7_0/LegacyManagementControllerTest.java       | 105 ++++
 .../v7_0/category/ContainerControllerTest.java     |  83 +++
 .../LegacyCategoryControllerFactoryTest.java       |  58 +++
 24 files changed, 4024 insertions(+)

diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/AbstractLegacyConfiguredObjectController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/AbstractLegacyConfiguredObjectController.java
new file mode 100644
index 0000000..131beb8
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/AbstractLegacyConfiguredObjectController.java
@@ -0,0 +1,560 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createBadRequestManagementException;
+import static org.apache.qpid.server.management.plugin.ManagementException.createInternalServerErrorManagementException;
+import static org.apache.qpid.server.management.plugin.ManagementException.createNotFoundManagementException;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementRequest;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.RequestType;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public abstract class AbstractLegacyConfiguredObjectController extends AbstractManagementController
+        implements LegacyManagementController
+{
+    private final ManagementController _nextVersionManagementController;
+    private final String _modelVersion;
+
+    private final Map<String, String> _categoryNames = new HashMap<>();
+    private final Map<String, List<String>> _parents = new HashMap<>();
+    private final Map<String, List<String>> _children = new HashMap<>();
+
+    private final Map<String, CategoryController> _categoryConverters = new HashMap<>();
+    private final Map<String, Set<TypeController>> _typeControllers = new HashMap<>();
+    private volatile LegacyConfiguredObjectToMapConverter _legacyConfiguredObjectToMapConverter;
+
+    public AbstractLegacyConfiguredObjectController(final String modelVersion,
+                                                    final ManagementController nextVersionManagementController)
+    {
+        _modelVersion = modelVersion;
+        _nextVersionManagementController = nextVersionManagementController;
+    }
+
+    @Override
+    public String getVersion()
+    {
+        return _modelVersion;
+    }
+
+    @Override
+    public Collection<String> getCategories()
+    {
+        return Collections.unmodifiableCollection(_categoryNames.values());
+    }
+
+    @Override
+    public String getCategoryMapping(final String category)
+    {
+        return String.format("/api/v%s/%s/", getVersion(), category.toLowerCase());
+    }
+
+    @Override
+    public String getCategory(final ConfiguredObject<?> managedObject)
+    {
+        return _nextVersionManagementController.getCategory(managedObject);
+    }
+
+    @Override
+    public List<String> getCategoryHierarchy(final ConfiguredObject<?> root, final String categoryName)
+    {
+        return getCategoryHierarchy(getCategory(root), categoryName);
+    }
+
+    @Override
+    public ManagementController getNextVersionManagementController()
+    {
+        return _nextVersionManagementController;
+    }
+
+
+    @Override
+    public LegacyConfiguredObject createOrUpdate(final ConfiguredObject<?> root,
+                                                 final String category,
+                                                 final List<String> path,
+                                                 final Map<String, Object> attributes,
+                                                 final boolean isPost) throws ManagementException
+    {
+        return getCategoryController(category).createOrUpdate(root, path, attributes, isPost);
+    }
+
+
+    @Override
+    public Object get(final ConfiguredObject<?> root,
+                      final String category,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return getCategoryController(category).get(root, path, convertQueryParameters(parameters));
+    }
+
+    @Override
+    public int delete(final ConfiguredObject<?> root,
+                      final String category,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return getCategoryController(category).delete(root, path, convertQueryParameters(parameters));
+    }
+
+    @Override
+    public ManagementResponse invoke(final ConfiguredObject<?> root,
+                                     final String category,
+                                     final List<String> path,
+                                     final String operation,
+                                     final Map<String, Object> parameters,
+                                     final boolean isPost,
+                                     final boolean isSecureOrAllowedOnInsecureChannel) throws ManagementException
+    {
+        return getCategoryController(category).invoke(root, path, operation, parameters, isPost,
+                                                      isSecureOrAllowedOnInsecureChannel);
+    }
+
+    @Override
+    public Object getPreferences(final ConfiguredObject<?> root,
+                                 final String category,
+                                 final List<String> path,
+                                 final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return getCategoryController(category).getPreferences(root, path, convertQueryParameters(parameters));
+    }
+
+    @Override
+    public void setPreferences(final ConfiguredObject<?> root,
+                               final String category,
+                               final List<String> path,
+                               final Object preferences,
+                               final Map<String, List<String>> parameters,
+                               final boolean isPost) throws ManagementException
+    {
+        getCategoryController(category).setPreferences(root, path, preferences, parameters, isPost);
+    }
+
+    @Override
+    public int deletePreferences(final ConfiguredObject<?> root,
+                                 final String category,
+                                 final List<String> path,
+                                 final Map<String, List<String>> parameters) throws ManagementException
+    {
+
+        return getCategoryController(category).deletePreferences(root, path, convertQueryParameters(parameters));
+    }
+
+    @Override
+    public CategoryController getCategoryController(final String category)
+    {
+        CategoryController converter = _categoryConverters.get(category.toLowerCase());
+        if (converter == null)
+        {
+            throw createInternalServerErrorManagementException(String.format("Converter for type '%s' cannot be found ",
+                                                                             category));
+        }
+        return converter;
+    }
+
+    @Override
+    public Set<TypeController> getTypeControllersByCategory(final String name)
+    {
+        Set<TypeController> typeControllers = _typeControllers.get(name.toLowerCase());
+        if (typeControllers == null)
+        {
+            return Collections.emptySet();
+        }
+        return Collections.unmodifiableSet(typeControllers);
+    }
+
+    @Override
+    public List<String> getCategoryHierarchy(final String rootCategory,
+                                             final String categoryName)
+    {
+        if (!_categoryNames.containsKey(rootCategory.toLowerCase()))
+        {
+            throw createInternalServerErrorManagementException(String.format("Unsupported root category '%s'",
+                                                                             rootCategory));
+        }
+        if (!_categoryNames.containsKey(categoryName.toLowerCase()))
+        {
+            throw createInternalServerErrorManagementException(String.format("Unsupported category '%s'",
+                                                                             categoryName));
+        }
+        final List<String> hierarchyList = new ArrayList<>();
+
+        String category = _categoryNames.get(categoryName.toLowerCase());
+        if (!category.equals(rootCategory))
+        {
+            Collection<String> parentCategories;
+
+            hierarchyList.add(category);
+
+            while (!(parentCategories = _parents.get(category)).contains(rootCategory))
+            {
+                hierarchyList.addAll(parentCategories);
+                category = parentCategories.iterator().next();
+            }
+
+            Collections.reverse(hierarchyList);
+        }
+        return Collections.unmodifiableList(hierarchyList);
+    }
+
+    @Override
+    public Collection<String> getChildrenCategories(String category)
+    {
+        List<String> children = _children.get(_categoryNames.get(category.toLowerCase()));
+        if (children == null)
+        {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableList(children);
+    }
+
+    @Override
+    public Collection<String> getParentTypes(final String category)
+    {
+        return _parents.get(category);
+    }
+
+    public void initialize()
+    {
+        initialize(CategoryControllerFactory.findFactories(getVersion()),
+                   TypeControllerFactory.findFactories(getVersion()));
+    }
+
+    protected void initialize(final Set<CategoryControllerFactory> categoryFactories,
+                           final Set<TypeControllerFactory> typeFactories)
+    {
+        createTypeControllers(typeFactories);
+        createCategoryControllers(categoryFactories);
+        _legacyConfiguredObjectToMapConverter = new LegacyConfiguredObjectToMapConverter(this);
+    }
+
+
+    @Override
+    protected RequestType getRequestType(final ManagementRequest managementRequest) throws ManagementException
+    {
+        final List<String> path = managementRequest.getPath();
+        final String category = managementRequest.getCategory();
+        if (category == null)
+        {
+            throw createNotFoundManagementException(String.format("Category is not found for path '%s%s'",
+                                                                  getCategoryMapping(category),
+                                                                  buildPath(path)));
+        }
+
+        final List<String> hierarchy = getCategoryHierarchy(managementRequest.getRoot(), category);
+        return getManagementRequestType(managementRequest.getMethod(), category, path, hierarchy);
+    }
+
+    @Override
+    public LegacyConfiguredObject convertFromNextVersion(final LegacyConfiguredObject nextVersionObject)
+    {
+        return getCategoryController(nextVersionObject.getCategory()).convertFromNextVersion(nextVersionObject);
+    }
+
+    protected abstract Map<String, List<String>> convertQueryParameters(final Map<String, List<String>> parameters);
+
+    private void addRelationship(String parentCategory, String childCategory)
+    {
+        Collection<String> parents = _parents.computeIfAbsent(childCategory, k -> new ArrayList<>());
+        parents.add(parentCategory);
+
+        Collection<String> children = _children.computeIfAbsent(parentCategory, k -> new ArrayList<>());
+        children.add(childCategory);
+
+        _categoryNames.put(childCategory.toLowerCase(), childCategory);
+    }
+
+
+    private void createCategoryControllers(final Set<CategoryControllerFactory> factories)
+    {
+        factories.stream()
+                 .map(this::create)
+                 .flatMap(Collection::stream)
+                 .peek(this::register)
+                 .forEach(c -> {
+                     if (_categoryConverters.put(c.getCategory().toLowerCase(), c) != null)
+                     {
+                         throw new IllegalStateException(String.format(
+                                 "Category converter for category '%s' is already registered",
+                                 c.getCategory()));
+                     }
+                 });
+    }
+
+    private Set<CategoryController> create(final CategoryControllerFactory factory)
+    {
+        return factory.getSupportedCategories()
+                      .stream()
+                      .map(type -> factory.createController(type, this))
+                      .collect(Collectors.toSet());
+    }
+
+    private void register(final CategoryController converter)
+    {
+        String type = converter.getCategory();
+        for (String category : converter.getParentCategories())
+        {
+            addRelationship(category, type);
+        }
+        _categoryNames.put(type.toLowerCase(), type);
+    }
+
+    private void createTypeControllers(Set<TypeControllerFactory> typeFactories)
+    {
+        for (TypeControllerFactory factory : typeFactories)
+        {
+            TypeController controller = factory.createController(this);
+            Set<TypeController> categoryTypeConverters =
+                    _typeControllers.computeIfAbsent(factory.getCategory().toLowerCase(), k -> new HashSet<>());
+
+            categoryTypeConverters.add(controller);
+        }
+    }
+
+    protected LegacyConfiguredObjectToMapConverter getLegacyConfiguredObjectToMapConverter()
+    {
+        return _legacyConfiguredObjectToMapConverter;
+    }
+
+
+    private RequestType getManagementRequestType(final String method,
+                                                 final String category,
+                                                 final List<String> parts,
+                                                 final List<String> hierarchy)
+    {
+        if ("POST".equals(method))
+        {
+            return getPostRequestType(category, parts, hierarchy);
+        }
+        else if ("PUT".equals(method))
+        {
+            return getPutRequestType(category, parts, hierarchy);
+        }
+        else if ("GET".equals(method))
+        {
+            return getGetRequestType(category, parts, hierarchy);
+        }
+        else if ("DELETE".equals(method))
+        {
+            return getDeleteRequestType(category, parts, hierarchy);
+        }
+        else
+        {
+            throw createBadRequestManagementException(String.format("Unexpected method type '%s' for path '%s%s'",
+                                                                    method,
+                                                                    getCategoryMapping(category),
+                                                                    buildPath(parts)));
+        }
+    }
+
+    private RequestType getDeleteRequestType(final String category,
+                                             final List<String> parts,
+                                             final List<String> hierarchy)
+    {
+        if (parts.size() <= hierarchy.size())
+        {
+            return RequestType.MODEL_OBJECT;
+        }
+        else
+        {
+            if (USER_PREFERENCES.equals(parts.get(hierarchy.size())))
+            {
+                return RequestType.USER_PREFERENCES;
+            }
+        }
+        final String categoryMapping = getCategoryMapping(category);
+        final String expectedPath = buildExpectedPath(categoryMapping, hierarchy);
+        throw createBadRequestManagementException(String.format(
+                "Invalid DELETE path '%s%s'. Expected: '%s' or '%s/userpreferences[/<preference type>[/<preference name>]]'",
+                categoryMapping,
+                buildPath(parts),
+                expectedPath,
+                expectedPath));
+    }
+
+    private RequestType getGetRequestType(final String category,
+                                          final List<String> parts,
+                                          final List<String> hierarchy)
+    {
+        if (parts.size() <= hierarchy.size())
+        {
+            return RequestType.MODEL_OBJECT;
+        }
+        else
+        {
+            if (USER_PREFERENCES.equals(parts.get(hierarchy.size())))
+            {
+                return RequestType.USER_PREFERENCES;
+            }
+            else if (VISIBLE_USER_PREFERENCES.equals(parts.get(hierarchy.size())))
+            {
+                return RequestType.VISIBLE_PREFERENCES;
+            }
+            else if (parts.size() == hierarchy.size() + 1)
+            {
+                return RequestType.OPERATION;
+            }
+        }
+
+        final String categoryMapping = getCategoryMapping(category);
+        throw createBadRequestManagementException(String.format(
+                "Invalid GET path '%s%s'. Expected: '%s[/<operation name>]'",
+                categoryMapping,
+                buildPath(parts),
+                buildExpectedPath(categoryMapping, hierarchy)));
+    }
+
+    private RequestType getPutRequestType(final String category,
+                                          final List<String> parts,
+                                          final List<String> hierarchy)
+    {
+        if (parts.size() == hierarchy.size() || parts.size() == hierarchy.size() - 1)
+        {
+            return RequestType.MODEL_OBJECT;
+        }
+        else if (parts.size() > hierarchy.size() && USER_PREFERENCES.equals(parts.get(hierarchy.size())))
+        {
+            return RequestType.USER_PREFERENCES;
+        }
+        else
+        {
+            final String categoryMapping = getCategoryMapping(category);
+            throw createBadRequestManagementException(String.format("Invalid PUT path '%s%s'. Expected: '%s'",
+                                                                    categoryMapping, buildPath(parts),
+                                                                    buildExpectedPath(categoryMapping, hierarchy)));
+        }
+    }
+
+    private RequestType getPostRequestType(final String category,
+                                           final List<String> parts,
+                                           final List<String> hierarchy)
+    {
+        if (parts.size() == hierarchy.size() || parts.size() == hierarchy.size() - 1)
+        {
+            return RequestType.MODEL_OBJECT;
+        }
+        else if (parts.size() > hierarchy.size())
+        {
+            if (USER_PREFERENCES.equals(parts.get(hierarchy.size())))
+            {
+                return RequestType.USER_PREFERENCES;
+            }
+            else if (parts.size() == hierarchy.size() + 1
+                     && !VISIBLE_USER_PREFERENCES.equals(parts.get(hierarchy.size())))
+            {
+                return RequestType.OPERATION;
+            }
+        }
+
+        final String categoryMapping = getCategoryMapping(category);
+        final String expectedFullPath = buildExpectedPath(categoryMapping, hierarchy);
+        final String expectedParentPath =
+                buildExpectedPath(categoryMapping, hierarchy.subList(0, hierarchy.size() - 1));
+
+        throw createBadRequestManagementException(String.format(
+                "Invalid POST path '%s%s'. Expected: '%s/<operation name>'"
+                + " or '%s'"
+                + " or '%s/userpreferences[/<preference type>]'",
+                categoryMapping,
+                buildPath(parts),
+                expectedFullPath,
+                expectedParentPath,
+                expectedFullPath));
+    }
+
+    private String buildExpectedPath(final String servletPath, final List<String> hierarchy)
+    {
+        return hierarchy.stream()
+                        .map(h -> String.format("/<%s name>", h))
+                        .collect((Collectors.joining("", servletPath, "")));
+    }
+
+    private String buildPath(final List<String> path)
+    {
+        return path.isEmpty() ? "" : "/" + String.join("/", path);
+    }
+
+    protected Object formatConfiguredObject(final Object content,
+                                            final boolean isSecureOrAllowedOnInsecureChannel,
+                                            final int depth,
+                                            final int oversizeThreshold,
+                                            final boolean actuals,
+                                            final boolean excludeInheritedContext, final boolean responseAsList)
+    {
+        if (content instanceof LegacyConfiguredObject)
+        {
+            Object object = convertObject(
+                    (LegacyConfiguredObject) content,
+                    depth,
+                    actuals,
+                    oversizeThreshold,
+                    isSecureOrAllowedOnInsecureChannel,
+                    excludeInheritedContext);
+            return responseAsList ? Collections.singletonList(object) : object;
+        }
+        else if (content instanceof Collection)
+        {
+            Collection<Map<String, Object>> results = ((Collection<?>) content).stream()
+                                                                               .filter(o -> o instanceof LegacyConfiguredObject)
+                                                                               .map(LegacyConfiguredObject.class::cast)
+                                                                               .map(o -> convertObject(
+                                                                                       o,
+                                                                                       depth,
+                                                                                       actuals,
+                                                                                       oversizeThreshold,
+                                                                                       isSecureOrAllowedOnInsecureChannel,
+                                                                                       excludeInheritedContext))
+                                                                               .collect(Collectors.toSet());
+            if (!results.isEmpty())
+            {
+                return results;
+            }
+        }
+        return content;
+    }
+
+    protected Map<String, Object> convertObject(final LegacyConfiguredObject legacyConfiguredObjectObject,
+                                                final int depth,
+                                                final boolean actuals,
+                                                final int oversizeThreshold,
+                                                final boolean isSecureOrConfidentialOperationAllowedOnInsecureChannel,
+                                                final boolean excludeInheritedContext)
+    {
+        return getLegacyConfiguredObjectToMapConverter().convertManageableToMap(legacyConfiguredObjectObject,
+                                                                                depth,
+                                                                                actuals,
+                                                                                oversizeThreshold,
+                                                                                excludeInheritedContext);
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/CategoryController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/CategoryController.java
new file mode 100644
index 0000000..9e2c011
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/CategoryController.java
@@ -0,0 +1,78 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public interface CategoryController
+{
+    String getCategory();
+
+    String getNextVersionCategory();
+
+    String getDefaultType();
+
+    String[] getParentCategories();
+
+    LegacyManagementController getManagementController();
+
+    Object get(ConfiguredObject<?> root,
+               List<String> path,
+               Map<String, List<String>> parameters) throws ManagementException;
+
+    LegacyConfiguredObject createOrUpdate(ConfiguredObject<?> root,
+                                          List<String> path,
+                                          Map<String, Object> attributes,
+                                          boolean isPost) throws ManagementException;
+
+    int delete(ConfiguredObject<?> root,
+               List<String> path,
+               Map<String, List<String>> parameters) throws ManagementException;
+
+    ManagementResponse invoke(ConfiguredObject<?> root,
+                              List<String> path,
+                              String operation,
+                              Map<String, Object> parameters,
+                              boolean isPost,
+                              final boolean isSecure) throws ManagementException;
+
+    Object getPreferences(ConfiguredObject<?> root,
+                          List<String> path,
+                          Map<String, List<String>> parameters) throws ManagementException;
+
+    void setPreferences(ConfiguredObject<?> root,
+                        List<String> path,
+                        Object preferences,
+                        Map<String, List<String>> parameters,
+                        boolean isPost) throws ManagementException;
+
+    int deletePreferences(ConfiguredObject<?> root,
+                          List<String> path,
+                          Map<String, List<String>> parameters) throws ManagementException;
+
+    LegacyConfiguredObject convertFromNextVersion(LegacyConfiguredObject nextVersionObject);
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/CategoryControllerFactory.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/CategoryControllerFactory.java
new file mode 100644
index 0000000..552b2cd
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/CategoryControllerFactory.java
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.plugin.Pluggable;
+import org.apache.qpid.server.plugin.QpidServiceLoader;
+
+public interface CategoryControllerFactory extends Pluggable
+{
+    CategoryController createController(String type, LegacyManagementController managementController);
+
+    Set<String> getSupportedCategories();
+
+    String getModelVersion();
+
+    static Set<CategoryControllerFactory> findFactories(String version)
+    {
+        final Iterable<CategoryControllerFactory> factories =
+                new QpidServiceLoader().atLeastOneInstanceOf(CategoryControllerFactory.class);
+
+        return StreamSupport.stream(factories.spliterator(), false)
+                            .filter(f -> version.equals(f.getModelVersion()))
+                            .collect(Collectors.toSet());
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/ControllerManagementResponse.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/ControllerManagementResponse.java
new file mode 100644
index 0000000..ff3061f
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/ControllerManagementResponse.java
@@ -0,0 +1,82 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+
+public class ControllerManagementResponse implements ManagementResponse
+{
+    private final ResponseType _type;
+    private final Object _body;
+    private final int _status;
+    private final Map<String, String> _headers;
+
+    public ControllerManagementResponse(final ResponseType type, final Object body)
+    {
+        this(type, body, HttpServletResponse.SC_OK, Collections.emptyMap());
+    }
+
+    public ControllerManagementResponse(final ResponseType type,
+                                        final Object body,
+                                        final int status,
+                                        final Map<String, String> headers)
+    {
+        _type = type;
+        _body = body;
+        _status = status;
+        _headers = headers;
+    }
+
+    @Override
+    public ResponseType getType()
+    {
+        return _type;
+    }
+
+    @Override
+    public Object getBody()
+    {
+        return _body;
+    }
+
+    public int getStatus()
+    {
+        return _status;
+    }
+
+    @Override
+    public Map<String, String> getHeaders()
+    {
+        return _headers;
+    }
+
+    @Override
+    public int getResponseCode()
+    {
+        return _status;
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/GenericCategoryController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/GenericCategoryController.java
new file mode 100644
index 0000000..35ff45b
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/GenericCategoryController.java
@@ -0,0 +1,266 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createBadRequestManagementException;
+import static org.apache.qpid.server.management.plugin.ManagementException.createInternalServerErrorManagementException;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public abstract class GenericCategoryController implements CategoryController
+{
+    private final String _name;
+    private final LegacyManagementController _managementController;
+    private final ManagementController _nextVersionManagementController;
+    private final String _defaultType;
+    private final Map<String, TypeController> _typeControllers;
+    private final Map<String, TypeController> _nextVersionTypeControllers;
+
+    protected GenericCategoryController(final LegacyManagementController managementController,
+                                        final ManagementController nextVersionManagementController,
+                                        final String name,
+                                        final String defaultType,
+                                        final Set<TypeController> typeControllers)
+    {
+        _name = name;
+        _managementController = managementController;
+        _nextVersionManagementController = nextVersionManagementController;
+        _defaultType = defaultType;
+        _typeControllers = typeControllers.stream().collect(Collectors.toMap(TypeController::getTypeName, c -> c));
+        _nextVersionTypeControllers =
+                typeControllers.stream().collect(Collectors.toMap(TypeController::getNextVersionTypeName, c -> c));
+    }
+
+    @Override
+    public String getCategory()
+    {
+        return _name;
+    }
+
+    @Override
+    public String getNextVersionCategory()
+    {
+        return _name;
+    }
+
+    @Override
+    public String getDefaultType()
+    {
+        return _defaultType;
+    }
+
+    @Override
+    public LegacyManagementController getManagementController()
+    {
+        return _managementController;
+    }
+
+    @Override
+    public Object get(final ConfiguredObject<?> root,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters)
+            throws ManagementException
+    {
+        final Object content =
+                _nextVersionManagementController.get(root,
+                                                     getNextVersionCategory(),
+                                                     path,
+                                                     convertQueryParametersToNextVersion(parameters));
+        return convert(content);
+    }
+
+    @Override
+    public LegacyConfiguredObject createOrUpdate(ConfiguredObject<?> root,
+                                                 List<String> path,
+                                                 Map<String, Object> attributes,
+                                                 boolean isPost) throws ManagementException
+    {
+        final Map<String, Object> body = convertAttributesToNextVersion(root, path, attributes);
+        final Object configuredObject =
+                _nextVersionManagementController.createOrUpdate(root, getNextVersionCategory(), path, body, isPost);
+        if (configuredObject instanceof LegacyConfiguredObject)
+        {
+            LegacyConfiguredObject object = (LegacyConfiguredObject) configuredObject;
+            return convertFromNextVersion(object);
+        }
+        return null;
+    }
+
+    @Override
+    public LegacyConfiguredObject convertFromNextVersion(final LegacyConfiguredObject object)
+    {
+        TypeController controller = getTypeController(object);
+        if (controller != null)
+        {
+            return controller.convertFromNextVersion(object);
+        }
+        return convertNextVersionLegacyConfiguredObject(object);
+    }
+
+    @Override
+    public int delete(final ConfiguredObject<?> root,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return _nextVersionManagementController.delete(root,
+                                                       getNextVersionCategory(),
+                                                       path,
+                                                       convertQueryParametersToNextVersion(parameters));
+    }
+
+    @Override
+    public ManagementResponse invoke(final ConfiguredObject<?> root,
+                                     final List<String> path,
+                                     final String operation,
+                                     final Map<String, Object> parameters,
+                                     final boolean isPost,
+                                     final boolean isSecure) throws ManagementException
+    {
+
+        Object result = get(root, path, Collections.emptyMap());
+        if (result instanceof LegacyConfiguredObject)
+        {
+            final LegacyConfiguredObject legacyConfiguredObject = (LegacyConfiguredObject) result;
+            return legacyConfiguredObject.invoke(operation, parameters, isSecure);
+        }
+        else
+        {
+            throw createBadRequestManagementException(String.format("Configured object %s/%s is not found",
+                                                                    getManagementController().getCategoryMapping(
+                                                                            getCategory()),
+                                                                    String.join("/", path)));
+        }
+    }
+
+    @Override
+    public Object getPreferences(final ConfiguredObject<?> root,
+                                 final List<String> path,
+                                 final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return _nextVersionManagementController.getPreferences(root,
+                                                               getNextVersionCategory(),
+                                                               path,
+                                                               parameters);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void setPreferences(final ConfiguredObject<?> root,
+                               final List<String> path,
+                               final Object preferences,
+                               final Map<String, List<String>> parameters,
+                               final boolean isPost) throws ManagementException
+    {
+        _nextVersionManagementController.setPreferences(root,
+                                                        getNextVersionCategory(),
+                                                        path,
+                                                        preferences,
+                                                        parameters,
+                                                        isPost);
+    }
+
+    @Override
+    public int deletePreferences(final ConfiguredObject<?> root,
+                                 final List<String> path,
+                                 final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return _nextVersionManagementController.deletePreferences(root,
+                                                                  getNextVersionCategory(),
+                                                                  path,
+                                                                  parameters);
+    }
+
+    protected abstract LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object);
+
+    protected Map<String, List<String>> convertQueryParametersToNextVersion(final Map<String, List<String>> parameters)
+    {
+        return parameters;
+    }
+
+    protected ManagementController getNextVersionManagementController()
+    {
+        return _nextVersionManagementController;
+    }
+
+    protected Map<String, Object> convertAttributesToNextVersion(final ConfiguredObject<?> root,
+                                                                 final List<String> path,
+                                                                 final Map<String, Object> attributes)
+    {
+        TypeController typeController = getTypeController(attributes);
+        if (typeController != null)
+        {
+            return typeController.convertAttributesToNextVersion(root, path, attributes);
+        }
+        return attributes;
+    }
+
+
+    private Object convert(final Object content)
+    {
+        if (content instanceof LegacyConfiguredObject)
+        {
+            return convertFromNextVersion((LegacyConfiguredObject) content);
+        }
+        else if (content instanceof Collection)
+        {
+            final Collection<?> items = (Collection<?>) content;
+            return items.stream()
+                        .filter(LegacyConfiguredObject.class::isInstance)
+                        .map(LegacyConfiguredObject.class::cast)
+                        .map(this::convertFromNextVersion)
+                        .collect(Collectors.toList());
+        }
+        else
+        {
+            throw createInternalServerErrorManagementException("Unexpected data format from next version");
+        }
+    }
+
+    private TypeController getTypeController(final Map<String, Object> attributes)
+    {
+        String type = (String) attributes.get(LegacyConfiguredObject.TYPE);
+        if (type == null)
+        {
+            type = getDefaultType();
+        }
+        if (type != null)
+        {
+            return _typeControllers.get(type);
+        }
+        return null;
+    }
+
+    protected TypeController getTypeController(final LegacyConfiguredObject object)
+    {
+        String type = (String) object.getAttribute(LegacyConfiguredObject.TYPE);
+        return _nextVersionTypeControllers.get(type);
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/GenericLegacyConfiguredObject.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/GenericLegacyConfiguredObject.java
new file mode 100644
index 0000000..678cef0
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/GenericLegacyConfiguredObject.java
@@ -0,0 +1,195 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+
+public class GenericLegacyConfiguredObject implements LegacyConfiguredObject
+{
+    private final LegacyManagementController _managementController;
+    private final LegacyConfiguredObject _nextVersionLegacyConfiguredObject;
+    private final String _category;
+
+    public GenericLegacyConfiguredObject(final LegacyManagementController managementController,
+                                         final LegacyConfiguredObject nextVersionLegacyConfiguredObject,
+                                         final String category)
+    {
+        _nextVersionLegacyConfiguredObject = nextVersionLegacyConfiguredObject;
+        _managementController = managementController;
+        _category = category;
+    }
+
+    @Override
+    public Collection<String> getAttributeNames()
+    {
+        return _nextVersionLegacyConfiguredObject.getAttributeNames();
+    }
+
+    @Override
+    public Object getAttribute(final String name)
+    {
+        return convertLegacyConfiguredObjectIfRequired(_nextVersionLegacyConfiguredObject.getAttribute(name));
+    }
+
+    @Override
+    public Object getActualAttribute(final String name)
+    {
+        return _nextVersionLegacyConfiguredObject.getActualAttribute(name);
+    }
+
+    @Override
+    public Collection<LegacyConfiguredObject> getChildren(final String category)
+    {
+        final Collection<LegacyConfiguredObject> children =
+                _nextVersionLegacyConfiguredObject.getChildren(category);
+        if (children != null)
+        {
+            return children.stream().map(_managementController::convertFromNextVersion).collect(Collectors.toSet());
+        }
+        return Collections.emptySet();
+    }
+
+    @Override
+    public String getCategory()
+    {
+        return _category;
+    }
+
+    @Override
+    public ManagementResponse invoke(final String operation,
+                                     final Map<String, Object> parameters,
+                                     final boolean isSecure)
+    {
+        ManagementResponse result = _nextVersionLegacyConfiguredObject.invoke(operation, parameters, isSecure);
+
+        return convertLegacyConfiguredObjectIfRequired(result);
+    }
+
+    @Override
+    public LegacyConfiguredObject getNextVersionConfiguredObject()
+    {
+        return _nextVersionLegacyConfiguredObject;
+    }
+
+    @Override
+    public LegacyConfiguredObject getParent(final String category)
+    {
+        LegacyConfiguredObject parent = _nextVersionLegacyConfiguredObject.getParent(category);
+        return _managementController.convertFromNextVersion(parent);
+    }
+
+    @Override
+    public boolean isSecureAttribute(final String name)
+    {
+        return _nextVersionLegacyConfiguredObject.isSecureAttribute(name);
+    }
+
+    @Override
+    public boolean isOversizedAttribute(final String name)
+    {
+        return _nextVersionLegacyConfiguredObject.isOversizedAttribute(name);
+    }
+
+    @Override
+    public String getContextValue(final String contextKey)
+    {
+        return _nextVersionLegacyConfiguredObject.getContextValue(contextKey);
+    }
+
+    @Override
+    public Map<String, Object> getStatistics()
+    {
+        return _nextVersionLegacyConfiguredObject.getStatistics();
+    }
+
+    @Override
+    public boolean equals(final Object o)
+    {
+        if (this == o)
+        {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass())
+        {
+            return false;
+        }
+        final GenericLegacyConfiguredObject
+                that = (GenericLegacyConfiguredObject) o;
+        return Objects.equals(_nextVersionLegacyConfiguredObject, that._nextVersionLegacyConfiguredObject);
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash(_nextVersionLegacyConfiguredObject);
+    }
+
+    public LegacyManagementController getManagementController()
+    {
+        return _managementController;
+    }
+
+    protected LegacyConfiguredObject getNextVersionLegacyConfiguredObject()
+    {
+        return _nextVersionLegacyConfiguredObject;
+    }
+
+    private Object convertLegacyConfiguredObjectIfRequired(final Object value)
+    {
+        if (value instanceof LegacyConfiguredObject)
+        {
+            return _managementController.convertFromNextVersion((LegacyConfiguredObject) value);
+        }
+        else if (value instanceof Collection)
+        {
+            Collection<?> collection = (Collection<?>) value;
+            if (collection.size() > 0 && collection.iterator().next() instanceof LegacyConfiguredObject)
+            {
+                return collection.stream()
+                                 .filter(o -> o instanceof LegacyConfiguredObject)
+                                 .map(LegacyConfiguredObject.class::cast)
+                                 .map(_managementController::convertFromNextVersion)
+                                 .collect(Collectors.toSet());
+            }
+        }
+        return value;
+    }
+
+    private ManagementResponse convertLegacyConfiguredObjectIfRequired(final ManagementResponse response)
+    {
+        if (response.getType() == ResponseType.MODEL_OBJECT)
+        {
+            Object body = convertLegacyConfiguredObjectIfRequired(response.getBody());
+            return new ControllerManagementResponse(response.getType(),
+                                                    body,
+                                                    response.getResponseCode(),
+                                                    response.getHeaders());
+        }
+        return response;
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyConfiguredObject.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyConfiguredObject.java
new file mode 100644
index 0000000..47d293d
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyConfiguredObject.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+
+public interface LegacyConfiguredObject
+{
+    String ID = "id";
+    String NAME = "name";
+    String TYPE = "type";
+    String DESCRIPTION = "description";
+    String DURABLE = "durable";
+    String CONTEXT = "context";
+    String LIFETIME_POLICY = "lifetimePolicy";
+    String STATE = "state";
+    String DESIRED_STATE = "desiredState";
+
+    String LAST_UPDATED_BY = "lastUpdatedBy";
+    String LAST_UPDATED_TIME = "lastUpdatedTime";
+    String CREATED_BY = "createdBy";
+    String CREATED_TIME = "createdTime";
+    String LAST_OPENED_TIME = "lastOpenedTime";
+
+    Set<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+            ID,
+            NAME,
+            TYPE,
+            DESCRIPTION,
+            DURABLE,
+            CONTEXT,
+            LIFETIME_POLICY,
+            STATE,
+            DESIRED_STATE,
+            LAST_OPENED_TIME,
+            LAST_UPDATED_BY,
+            LAST_UPDATED_TIME,
+            CREATED_BY,
+            CREATED_TIME)));
+
+    Collection<String> getAttributeNames();
+
+    Object getAttribute(String name);
+
+    Map<String, Object> getStatistics();
+
+    Object getActualAttribute(String name);
+
+    boolean isSecureAttribute(String name);
+
+    boolean isOversizedAttribute(String name);
+
+    String getCategory();
+
+    Collection<LegacyConfiguredObject> getChildren(String category);
+
+    LegacyConfiguredObject getParent(String category);
+
+    String getContextValue(String contextKey);
+
+    ManagementResponse invoke(String operation, Map<String, Object> parameters, boolean isSecure);
+
+    LegacyConfiguredObject getNextVersionConfiguredObject();
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyConfiguredObjectToMapConverter.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyConfiguredObjectToMapConverter.java
new file mode 100644
index 0000000..f340359
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyConfiguredObjectToMapConverter.java
@@ -0,0 +1,251 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.model.Named;
+
+class LegacyConfiguredObjectToMapConverter
+{
+    private static final String STATISTICS_MAP_KEY = "statistics";
+    private static final String NAME = "name";
+    private static final String CONTEXT = "context";
+
+    private final LegacyManagementController _managementMetadata;
+
+    LegacyConfiguredObjectToMapConverter(final LegacyManagementController managementMetadata)
+    {
+        _managementMetadata = managementMetadata;
+    }
+
+    Map<String, Object> convertManageableToMap(final LegacyConfiguredObject legacyConfiguredObjectObject,
+                                               final int depth,
+                                               final boolean actuals,
+                                               final int oversizeThreshold,
+                                               final boolean excludeInheritedContext)
+    {
+        final Map<String, Object> object = new LinkedHashMap<>();
+
+        incorporateAttributesIntoMap(legacyConfiguredObjectObject,
+                                     object,
+                                     actuals,
+                                     excludeInheritedContext,
+                                     oversizeThreshold);
+        incorporateStatisticsIntoMap(legacyConfiguredObjectObject, object);
+
+        if (depth > 0)
+        {
+            incorporateChildrenIntoMap(legacyConfiguredObjectObject,
+                                       object,
+                                       depth,
+                                       actuals,
+                                       oversizeThreshold,
+                                       excludeInheritedContext);
+        }
+        return object;
+    }
+
+    private void incorporateAttributesIntoMap(
+            final LegacyConfiguredObject confObject,
+            final Map<String, Object> object,
+            final boolean useActualValues,
+            final boolean excludeInheritedContext,
+            final int oversizeThreshold)
+    {
+
+        for (String name : confObject.getAttributeNames())
+        {
+            Object value = useActualValues ? confObject.getActualAttribute(name) : confObject.getAttribute(name);
+            if (value instanceof LegacyConfiguredObject)
+            {
+                object.put(name, ((LegacyConfiguredObject) value).getAttribute(NAME));
+            }
+            else if (CONTEXT.equals(name))
+            {
+                Map<String, Object> contextValues = collectContext(confObject,
+                                                                   excludeInheritedContext,
+                                                                   useActualValues);
+
+                if (!contextValues.isEmpty())
+                {
+                    object.put(CONTEXT, contextValues);
+                }
+            }
+            else if (value instanceof Collection)
+            {
+                List<Object> converted = new ArrayList<>();
+                for (Object member : (Collection) value)
+                {
+                    if (member instanceof LegacyConfiguredObject)
+                    {
+                        converted.add(((LegacyConfiguredObject) member).getAttribute(NAME));
+                    }
+                    else
+                    {
+                        converted.add(member);
+                    }
+                }
+                object.put(name, converted);
+            }
+            else if (value instanceof Named)
+            {
+                object.put(name, ((Named) value).getName());
+            }
+            else if (value != null)
+            {
+                if (confObject.isSecureAttribute(name))
+                {
+                    value = confObject.getAttribute(name);
+                }
+
+                if (confObject.isOversizedAttribute(name) && !useActualValues)
+                {
+                    String valueString = String.valueOf(value);
+                    if (valueString.length() > oversizeThreshold)
+                    {
+                        object.put(name, String.valueOf(value).substring(0, oversizeThreshold - 4) + "...");
+                    }
+                    else
+                    {
+                        object.put(name, value);
+                    }
+                }
+                else
+                {
+                    object.put(name, value);
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> collectContext(final LegacyConfiguredObject configuredObject,
+                                               final boolean excludeInheritedContext,
+                                               final boolean useActualValues)
+    {
+        final Map<String, Object> actualContext = new HashMap<>();
+        if (excludeInheritedContext)
+        {
+            final Object value = configuredObject.getActualAttribute(CONTEXT);
+            if (value instanceof Map)
+            {
+                actualContext.putAll((Map<String, String>) value);
+            }
+        }
+        else
+        {
+            actualContext.putAll(System.getenv());
+            actualContext.putAll((Map) System.getProperties());
+            collectInheritedActualContext(configuredObject, actualContext);
+        }
+
+        if (useActualValues)
+        {
+            return actualContext;
+        }
+        else
+        {
+            final Map<String, Object> effectiveContext = new HashMap<>();
+            for (String contextKey : actualContext.keySet())
+            {
+                effectiveContext.put(contextKey, configuredObject.getContextValue(contextKey));
+            }
+            return effectiveContext;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void collectInheritedActualContext(final LegacyConfiguredObject confObject,
+                                               final Map<String, Object> contextValues)
+    {
+        final Collection<String> parents = _managementMetadata.getParentTypes(confObject.getCategory());
+        if (parents != null && !parents.isEmpty())
+        {
+            final LegacyConfiguredObject parent = confObject.getParent(parents.iterator().next());
+            if (parent != null)
+            {
+                collectInheritedActualContext(parent, contextValues);
+            }
+        }
+        final Object value = confObject.getActualAttribute(CONTEXT);
+        if (value instanceof Map)
+        {
+            contextValues.putAll((Map<String, Object>) value);
+        }
+    }
+
+    private void incorporateStatisticsIntoMap(final LegacyConfiguredObject confObject, final Map<String, Object> object)
+    {
+        final Map<String, Object> statMap = new TreeMap<>(confObject.getStatistics());
+        if (!statMap.isEmpty())
+        {
+            object.put(STATISTICS_MAP_KEY, statMap);
+        }
+    }
+
+    private void incorporateChildrenIntoMap(
+            final LegacyConfiguredObject confObject,
+            final Map<String, Object> object,
+            final int depth,
+            final boolean actuals,
+            final int oversizeThreshold,
+            final boolean excludeInheritedContext)
+    {
+        Collection<String> childTypes = _managementMetadata.getChildrenCategories(confObject.getCategory());
+        if (childTypes != null && !childTypes.isEmpty())
+        {
+            List<String> types = new ArrayList<>(childTypes);
+            Collections.sort(types);
+
+            for (String childType : types)
+            {
+                Collection<LegacyConfiguredObject> children = confObject.getChildren(childType);
+                if (children != null && !children.isEmpty())
+                {
+                    List<LegacyConfiguredObject> sortedChildren = new ArrayList<>(children);
+                    sortedChildren.sort(Comparator.comparing(o -> ((String) o.getAttribute(NAME))));
+
+                    List<Map<String, Object>> childObjects = sortedChildren.stream()
+                                                                           .sorted(Comparator.comparing(o -> ((String) o.getAttribute(NAME))))
+                                                                           .map(child -> convertManageableToMap(child,
+                                                                                                                depth
+                                                                                                                - 1,
+                                                                                                                actuals,
+                                                                                                                oversizeThreshold,
+                                                                                                                excludeInheritedContext))
+                                                                           .collect(Collectors.toList());
+                    String childTypeSingular = childType.toLowerCase();
+                    object.put(childTypeSingular + (childTypeSingular.endsWith("s") ? "es" : "s"), childObjects);
+                }
+            }
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyManagementController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyManagementController.java
new file mode 100644
index 0000000..4c9f4206
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/LegacyManagementController.java
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.Collection;
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+
+public interface LegacyManagementController extends ManagementController
+{
+    Collection<String> getParentTypes(String category);
+
+    Collection<String> getChildrenCategories(String category);
+
+    Collection<String> getCategories();
+
+    Collection<String> getCategoryHierarchy(String rootCategory, String category);
+
+    CategoryController getCategoryController(String category);
+
+    Set<TypeController> getTypeControllersByCategory(String name);
+
+    LegacyConfiguredObject convertFromNextVersion(LegacyConfiguredObject nextVersionObject);
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/TypeController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/TypeController.java
new file mode 100644
index 0000000..cce2eb7
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/TypeController.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public interface TypeController
+{
+    String getTypeName();
+
+    String getNextVersionTypeName();
+
+    Map<String, Object> convertAttributesToNextVersion(ConfiguredObject<?> root,
+                                                       List<String> path,
+                                                       Map<String, Object> attributes);
+
+    LegacyConfiguredObject convertFromNextVersion(LegacyConfiguredObject object);
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/TypeControllerFactory.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/TypeControllerFactory.java
new file mode 100644
index 0000000..2d3ec05
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/TypeControllerFactory.java
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.plugin.Pluggable;
+import org.apache.qpid.server.plugin.QpidServiceLoader;
+
+public interface TypeControllerFactory extends Pluggable
+{
+    TypeController createController(ManagementController managementController);
+
+    String getCategory();
+
+    String getNextVersionCategory();
+
+    String getModelVersion();
+
+    static Set<TypeControllerFactory> findFactories(String version)
+    {
+        final Iterable<TypeControllerFactory> factories =
+                new QpidServiceLoader().instancesOf(TypeControllerFactory.class);
+
+        return StreamSupport.stream(factories.spliterator(), false)
+                            .filter(f -> version.equals(f.getModelVersion()))
+                            .collect(Collectors.toSet());
+    }
+
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerAdapter.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerAdapter.java
new file mode 100644
index 0000000..e0da225
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerAdapter.java
@@ -0,0 +1,489 @@
+/*
+ *
+ * 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.management.plugin.controller.latest;
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createNotFoundManagementException;
+import static org.apache.qpid.server.model.ConfiguredObjectTypeRegistry.getCollectionMemberType;
+import static org.apache.qpid.server.model.ConfiguredObjectTypeRegistry.returnsCollectionOfConfiguredObjects;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementRequest;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+import org.apache.qpid.server.management.plugin.controller.ControllerManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectAttribute;
+import org.apache.qpid.server.model.ConfiguredObjectOperation;
+import org.apache.qpid.server.model.Model;
+
+public class LatestManagementControllerAdapter implements ManagementController
+{
+    private ManagementController _latestManagementController;
+
+    public LatestManagementControllerAdapter(final ManagementController latestManagementController)
+    {
+        _latestManagementController = latestManagementController;
+    }
+
+    @Override
+    public String getVersion()
+    {
+        return _latestManagementController.getVersion();
+    }
+
+    @Override
+    public Collection<String> getCategories()
+    {
+        return _latestManagementController.getCategories();
+    }
+
+    @Override
+    public String getCategoryMapping(final String category)
+    {
+        return _latestManagementController.getCategoryMapping(category);
+    }
+
+    @Override
+    public String getCategory(final ConfiguredObject<?> managedObject)
+    {
+        return _latestManagementController.getCategory(managedObject);
+    }
+
+    @Override
+    public Collection<String> getCategoryHierarchy(final ConfiguredObject<?> root, final String category)
+    {
+        return _latestManagementController.getCategoryHierarchy(root, category);
+    }
+
+    @Override
+    public ManagementController getNextVersionManagementController()
+    {
+        return _latestManagementController;
+    }
+
+    @Override
+    public ManagementResponse handleGet(final ManagementRequest request) throws ManagementException
+    {
+        ManagementResponse response = _latestManagementController.handleGet(request);
+        return new ControllerManagementResponse(response.getType(),
+                                                convertResponseObject(response.getBody()),
+                                                response.getResponseCode(),
+                                                response.getHeaders());
+    }
+
+    @Override
+    public ManagementResponse handlePut(final ManagementRequest request) throws ManagementException
+    {
+        ManagementResponse response = _latestManagementController.handlePut(request);
+        return new ControllerManagementResponse(response.getType(),
+                                                convertResponseObject(response.getBody()),
+                                                response.getResponseCode(),
+                                                response.getHeaders());
+    }
+
+    @Override
+    public ManagementResponse handlePost(final ManagementRequest request) throws ManagementException
+    {
+        ManagementResponse response = _latestManagementController.handlePost(request);
+        return new ControllerManagementResponse(response.getType(),
+                                                convertResponseObject(response.getBody()),
+                                                response.getResponseCode(),
+                                                response.getHeaders());
+    }
+
+    @Override
+    public ManagementResponse handleDelete(final ManagementRequest request) throws ManagementException
+    {
+        ManagementResponse response = _latestManagementController.handleDelete(request);
+        return new ControllerManagementResponse(response.getType(),
+                                                convertResponseObject(response.getBody()),
+                                                response.getResponseCode(),
+                                                response.getHeaders());
+    }
+
+
+    @Override
+    public Object get(final ConfiguredObject<?> root,
+                      final String category,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        Object result = _latestManagementController.get(root, category, path, parameters);
+        return convertResponseObject(result);
+    }
+
+
+    @Override
+    public Object createOrUpdate(final ConfiguredObject<?> root,
+                                 final String category,
+                                 final List<String> path,
+                                 final Map<String, Object> attributes,
+                                 final boolean isPost) throws ManagementException
+    {
+        Object result = _latestManagementController.createOrUpdate(root, category, path, attributes, isPost);
+        if (result instanceof ConfiguredObject)
+        {
+            return new LegacyConfiguredObjectObject((ConfiguredObject) result);
+        }
+        return null;
+    }
+
+    @Override
+    public int delete(final ConfiguredObject<?> root,
+                      final String category,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return _latestManagementController.delete(root, category, path, parameters);
+    }
+
+    @Override
+    public ManagementResponse invoke(final ConfiguredObject<?> root,
+                                     final String category,
+                                     final List<String> path,
+                                     final String operationName,
+                                     final Map<String, Object> parameters,
+                                     final boolean isPost,
+                                     final boolean isSecureOrAllowedOnInsecureChannel) throws ManagementException
+    {
+        ManagementResponse response =
+                _latestManagementController.invoke(root, category, path, operationName, parameters, isPost,
+                                                   isSecureOrAllowedOnInsecureChannel);
+        if (response.getType() == ResponseType.MODEL_OBJECT)
+        {
+            Object result = response.getBody();
+
+            if (result instanceof ConfiguredObject)
+            {
+                result = new LegacyConfiguredObjectObject((ConfiguredObject<?>) result);
+            }
+            else if (result instanceof Collection)
+            {
+                result = ((Collection<?>) result).stream()
+                                                 .map(o -> new LegacyConfiguredObjectObject((ConfiguredObject<?>) o))
+                                                 .collect(Collectors.toSet());
+            }
+            return new ControllerManagementResponse(ResponseType.MODEL_OBJECT, result);
+        }
+        return response;
+    }
+
+    @Override
+    public Object getPreferences(final ConfiguredObject<?> root,
+                                 final String category,
+                                 final List<String> path,
+                                 final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return _latestManagementController.getPreferences(root, category, path, parameters);
+    }
+
+    @Override
+    public void setPreferences(final ConfiguredObject<?> root,
+                               final String category,
+                               final List<String> path,
+                               final Object preferences,
+                               final Map<String, List<String>> parameters,
+                               final boolean isPost) throws ManagementException
+    {
+        _latestManagementController.setPreferences(root, category, path, preferences, parameters, isPost);
+    }
+
+    @Override
+    public int deletePreferences(final ConfiguredObject<?> root,
+                                 final String category,
+                                 final List<String> path,
+                                 final Map<String, List<String>> parameters) throws ManagementException
+    {
+        return _latestManagementController.delete(root, category, path, parameters);
+    }
+
+    @Override
+    public Object formatConfiguredObject(final Object data,
+                                         final Map<String, List<String>> parameters,
+                                         final boolean isSecureOrAllowedOnInsecureChannel)
+    {
+        Object content = data;
+        if (content instanceof LegacyConfiguredObjectObject)
+        {
+
+            content = ((LegacyConfiguredObjectObject) data).getConfiguredObject();
+        }
+        else if (data instanceof Collection)
+        {
+            content = ((Collection<?>) data).stream()
+                                            .filter(o -> o instanceof LegacyConfiguredObjectObject)
+                                            .map(LegacyConfiguredObjectObject.class::cast)
+                                            .map(LegacyConfiguredObjectObject::getConfiguredObject)
+                                            .collect(Collectors.toSet());
+        }
+
+        return _latestManagementController.formatConfiguredObject(content,
+                                                                  parameters,
+                                                                  isSecureOrAllowedOnInsecureChannel);
+    }
+
+    private Class<? extends ConfiguredObject> getRequestCategoryClass(final String categoryName,
+                                                                      final Model model)
+    {
+        for (Class<? extends ConfiguredObject> category : model.getSupportedCategories())
+        {
+            if (category.getSimpleName().toLowerCase().equals(categoryName))
+            {
+                return category;
+            }
+        }
+        throw createNotFoundManagementException(String.format("Category is not found for '%s'", categoryName));
+    }
+
+    private Object convertResponseObject(final Object result)
+    {
+        if (result instanceof ConfiguredObject)
+        {
+            return new LegacyConfiguredObjectObject((ConfiguredObject) result);
+        }
+        else if (result instanceof Collection)
+        {
+            return ((Collection<?>) result).stream().filter(o -> o instanceof ConfiguredObject)
+                                           .map(ConfiguredObject.class::cast)
+                                           .map(o -> new LegacyConfiguredObjectObject((ConfiguredObject<?>) o))
+                                           .collect(Collectors.toSet());
+        }
+        return result;
+    }
+
+    private class LegacyConfiguredObjectObject implements LegacyConfiguredObject
+    {
+        private final Map<String, Object> _actualAttributes;
+        private ConfiguredObject<?> _configuredObject;
+
+        LegacyConfiguredObjectObject(final ConfiguredObject<?> configuredObject)
+        {
+            _configuredObject = configuredObject;
+            _actualAttributes = configuredObject.getActualAttributes();
+        }
+
+        @Override
+        public Collection<String> getAttributeNames()
+        {
+            return _configuredObject.getAttributeNames();
+        }
+
+        @Override
+        public Object getAttribute(final String name)
+        {
+            return convertIntoLegacyIfRequired(name, _configuredObject.getAttribute(name));
+        }
+
+        @Override
+        public Object getActualAttribute(final String name)
+        {
+            return _actualAttributes.get(name);
+        }
+
+        @Override
+        public Collection<LegacyConfiguredObject> getChildren(final String category)
+        {
+            Class<? extends ConfiguredObject> categoryClass =
+                    getRequestCategoryClass(category.toLowerCase(), _configuredObject.getModel());
+            return _configuredObject.getChildren(categoryClass)
+                                    .stream()
+                                    .map(LegacyConfiguredObjectObject::new)
+                                    .collect(Collectors.toSet());
+        }
+
+        @Override
+        public String getCategory()
+        {
+            return _configuredObject.getCategoryClass().getSimpleName();
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public ManagementResponse invoke(final String operationName,
+                                         final Map<String, Object> parameters,
+                                         final boolean isSecure)
+        {
+            try
+            {
+                final Model model = _configuredObject.getModel();
+                final Map<String, ConfiguredObjectOperation<?>> availableOperations =
+                        model.getTypeRegistry().getOperations(_configuredObject.getClass());
+                final ConfiguredObjectOperation operation = availableOperations.get(operationName);
+                if (operation == null)
+                {
+                    throw createNotFoundManagementException(String.format("No such operation as '%s' in '%s'",
+                                                                          operationName,
+                                                                          getCategory()));
+                }
+
+                Object returnValue = operation.perform(_configuredObject, parameters);
+
+                final ResponseType responseType;
+                if (ConfiguredObject.class.isAssignableFrom(operation.getReturnType()))
+                {
+                    returnValue = new LegacyConfiguredObjectObject((ConfiguredObject<?>) returnValue);
+                    responseType = ResponseType.MODEL_OBJECT;
+                }
+                else if (returnsCollectionOfConfiguredObjects(operation))
+                {
+                    returnValue = ((Collection) returnValue).stream()
+                                                            .map(o -> new LegacyConfiguredObjectObject((ConfiguredObject<?>) o))
+                                                            .collect(Collectors.toSet());
+                    responseType = ResponseType.MODEL_OBJECT;
+                }
+                else
+                {
+                    responseType = ResponseType.DATA;
+                }
+                return new ControllerManagementResponse(responseType, returnValue);
+            }
+            catch (RuntimeException e)
+            {
+                throw ManagementException.toManagementException(e,
+                                                                getCategoryMapping(getCategory()),
+                                                                Collections.emptyList());
+            }
+            catch (Error e)
+            {
+                throw ManagementException.handleError(e);
+            }
+        }
+
+        @Override
+        public LegacyConfiguredObject getNextVersionConfiguredObject()
+        {
+            return null;
+        }
+
+        @Override
+        public LegacyConfiguredObject getParent(final String category)
+        {
+            ConfiguredObject<?> parent = _configuredObject.getParent();
+            if (category != null && !parent.getCategoryClass().getSimpleName().equalsIgnoreCase(category))
+            {
+                throw new IllegalArgumentException(String.format(
+                        "ConfiguredObject of category '%s' has no parent of category %s",
+                        getCategory(),
+                        category));
+            }
+            return new LegacyConfiguredObjectObject(parent);
+        }
+
+        @Override
+        public boolean isSecureAttribute(final String name)
+        {
+            ConfiguredObjectAttribute<?, ?> objectAttribute = getConfiguredObjectAttribute(name);
+            return objectAttribute != null && objectAttribute.isSecure();
+        }
+
+        @Override
+        public boolean isOversizedAttribute(final String name)
+        {
+            ConfiguredObjectAttribute<?, ?> objectAttribute = getConfiguredObjectAttribute(name);
+            return objectAttribute != null && objectAttribute.isOversized();
+        }
+
+        @Override
+        public String getContextValue(final String contextKey)
+        {
+            return _configuredObject.getContextValue(String.class, contextKey);
+        }
+
+        @Override
+        public Map<String, Object> getStatistics()
+        {
+            return _configuredObject.getStatistics();
+        }
+
+        @Override
+        public boolean equals(final Object o)
+        {
+            if (this == o)
+            {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass())
+            {
+                return false;
+            }
+            final LegacyConfiguredObjectObject object = (LegacyConfiguredObjectObject) o;
+            return Objects.equals(_configuredObject, object._configuredObject);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return _configuredObject.hashCode();
+        }
+
+        ConfiguredObject<?> getConfiguredObject()
+        {
+            return _configuredObject;
+        }
+
+        private Object convertIntoLegacyIfRequired(final String name, final Object value)
+        {
+            if (value != null)
+            {
+                ConfiguredObjectAttribute<?, ?> attribute = getConfiguredObjectAttribute(name);
+
+                final Class<?> type = attribute.getType();
+                final Type genericType = attribute.getGenericType();
+                if (ConfiguredObject.class.isAssignableFrom(type))
+                {
+                    return new LegacyConfiguredObjectObject((ConfiguredObject<?>) value);
+                }
+                else if (Collection.class.isAssignableFrom(type)
+                         && genericType instanceof ParameterizedType
+                         && ConfiguredObject.class.isAssignableFrom(getCollectionMemberType((ParameterizedType) genericType)))
+                {
+                    Collection<?> collection = (Collection<?>) value;
+                    return collection.stream()
+                                     .filter(o -> o instanceof LegacyConfiguredObjectObject)
+                                     .map(LegacyConfiguredObjectObject.class::cast)
+                                     .map(co -> new LegacyConfiguredObjectObject((ConfiguredObject<?>) co))
+                                     .collect(Collectors.toSet());
+                }
+            }
+            return value;
+        }
+
+
+        private ConfiguredObjectAttribute<?, ?> getConfiguredObjectAttribute(final String name)
+        {
+            return _configuredObject.getModel()
+                                    .getTypeRegistry()
+                                    .getAttributeTypes(_configuredObject.getClass())
+                                    .get(name);
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementController.java
new file mode 100644
index 0000000..44ddf03
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementController.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * 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.management.plugin.controller.v7_0;
+
+import static org.apache.qpid.server.management.plugin.controller.ConverterHelper.getIntParameterFromRequest;
+import static org.apache.qpid.server.management.plugin.controller.ConverterHelper.getParameter;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.AbstractLegacyConfiguredObjectController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+
+public class LegacyManagementController extends AbstractLegacyConfiguredObjectController
+{
+    private static final String DEPTH_PARAM = "depth";
+    private static final String OVERSIZE_PARAM = "oversize";
+    private static final String ACTUALS_PARAM = "actuals";
+    private static final String EXCLUDE_INHERITED_CONTEXT_PARAM = "excludeInheritedContext";
+    private static final String SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST = "singletonModelObjectResponseAsList";
+
+    private static final int DEFAULT_DEPTH = 0;
+    private static final int DEFAULT_OVERSIZE = 120;
+
+    public LegacyManagementController(final ManagementController nextVersionManagementController)
+    {
+        super(LegacyManagementControllerFactory.MODEL_VERSION, nextVersionManagementController);
+    }
+
+    @Override
+    protected Map<String, List<String>> convertQueryParameters(final Map<String, List<String>> parameters)
+    {
+        return parameters;
+    }
+
+    @Override
+    public Object formatConfiguredObject(final Object content,
+                                         final Map<String, List<String>> parameters,
+                                         final boolean isSecureOrAllowedOnInsecureChannel)
+    {
+        final int depth = getIntParameterFromRequest(parameters, DEPTH_PARAM, DEFAULT_DEPTH);
+        final int oversizeThreshold = getIntParameterFromRequest(parameters, OVERSIZE_PARAM, DEFAULT_OVERSIZE);
+        final boolean actuals = Boolean.parseBoolean(getParameter(ACTUALS_PARAM, parameters));
+        final String excludeInheritedContextParameter = getParameter(EXCLUDE_INHERITED_CONTEXT_PARAM, parameters);
+        final boolean excludeInheritedContext = excludeInheritedContextParameter == null
+                                                || Boolean.parseBoolean(excludeInheritedContextParameter);
+        final boolean responseAsList =
+                Boolean.parseBoolean(getParameter(SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST, parameters));
+
+        return formatConfiguredObject(content,
+                                      isSecureOrAllowedOnInsecureChannel,
+                                      depth,
+                                      oversizeThreshold,
+                                      actuals,
+                                      excludeInheritedContext,
+                                      responseAsList);
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementControllerFactory.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementControllerFactory.java
new file mode 100644
index 0000000..1b26e92
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementControllerFactory.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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.management.plugin.controller.v7_0;
+
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementControllerFactory;
+import org.apache.qpid.server.management.plugin.controller.CategoryControllerFactory;
+import org.apache.qpid.server.management.plugin.controller.TypeControllerFactory;
+import org.apache.qpid.server.plugin.PluggableService;
+
+@PluggableService
+public class LegacyManagementControllerFactory implements ManagementControllerFactory
+{
+    public static final String MODEL_VERSION = "7.0";
+
+    @Override
+    public String getType()
+    {
+        return "org.apache.qpid.server.management.plugin.model.v7_0";
+    }
+
+    @Override
+    public String getVersion()
+    {
+        return MODEL_VERSION;
+    }
+
+    @Override
+    public String getPreviousVersion()
+    {
+        return "6.1";
+    }
+
+    @Override
+    public ManagementController createManagementController(final HttpManagementConfiguration<?> httpManagement,
+                                                           final ManagementController nextVersionManagementController)
+    {
+
+        LegacyManagementController controller = new LegacyManagementController(nextVersionManagementController);
+        controller.initialize();
+        return controller;
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/ContainerController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/ContainerController.java
new file mode 100644
index 0000000..44a34e5
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/ContainerController.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * 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.management.plugin.controller.v7_0.category;
+
+
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+
+
+class ContainerController extends LegacyCategoryController
+{
+    ContainerController(final LegacyManagementController legacyManagementController,
+                        final String type,
+                        final String parentType,
+                        final String defaultType,
+                        final Set<TypeController> typeControllers)
+    {
+        super(legacyManagementController, type, parentType, defaultType, typeControllers);
+    }
+
+    @Override
+    protected LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new LegacyContainer(getManagementController(), object, getCategory());
+    }
+
+    static class LegacyContainer extends GenericLegacyConfiguredObject
+    {
+        private static final String MODEL_VERSION = "modelVersion";
+
+        LegacyContainer(final LegacyManagementController managementController,
+                        final LegacyConfiguredObject nextVersionLegacyConfiguredObject,
+                        final String category)
+        {
+            super(managementController, nextVersionLegacyConfiguredObject, category);
+        }
+
+        @Override
+        public Object getAttribute(final String name)
+        {
+            if (MODEL_VERSION.equals(name))
+            {
+                return getManagementController().getVersion();
+            }
+            return super.getAttribute(name);
+        }
+
+        @Override
+        public Object getActualAttribute(final String name)
+        {
+            if (MODEL_VERSION.equals(name))
+            {
+                return getManagementController().getVersion();
+            }
+            return super.getActualAttribute(name);
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryController.java
new file mode 100644
index 0000000..82ace6d
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryController.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * 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.management.plugin.controller.v7_0.category;
+
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.controller.GenericCategoryController;
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+import org.apache.qpid.server.management.plugin.controller.latest.LatestManagementControllerAdapter;
+
+public class LegacyCategoryController extends GenericCategoryController
+{
+    private final String _parentCategory;
+
+    LegacyCategoryController(final LegacyManagementController managementController,
+                             final String name,
+                             final String parentCategory,
+                             final String defaultType,
+                             final Set<TypeController> typeControllers)
+    {
+        super(managementController,
+              new LatestManagementControllerAdapter(managementController.getNextVersionManagementController()),
+              name,
+              defaultType,
+              typeControllers);
+        _parentCategory = parentCategory;
+    }
+
+    @Override
+    public String[] getParentCategories()
+    {
+        return new String[]{_parentCategory};
+    }
+
+    @Override
+    protected LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new GenericLegacyConfiguredObject(getManagementController(),
+                                                 object,
+                                                 getCategory());
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryControllerFactory.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryControllerFactory.java
new file mode 100644
index 0000000..3f07cc3
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryControllerFactory.java
@@ -0,0 +1,154 @@
+/*
+ *
+ * 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.management.plugin.controller.v7_0.category;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+import org.apache.qpid.server.management.plugin.controller.CategoryControllerFactory;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.v7_0.LegacyManagementControllerFactory;
+import org.apache.qpid.server.plugin.PluggableService;
+
+@PluggableService
+public class LegacyCategoryControllerFactory implements CategoryControllerFactory
+{
+    static final String CATEGORY_BROKER = "Broker";
+    private static final String CATEGORY_BROKER_LOGGER = "BrokerLogger";
+    private static final String CATEGORY_BROKER_LOG_INCLUSION_RULE = "BrokerLogInclusionRule";
+    private static final String CATEGORY_AUTHENTICATION_PROVIDER = "AuthenticationProvider";
+    private static final String CATEGORY_USER = "User";
+    private static final String CATEGORY_ACCESS_CONTROL_PROVIDER = "AccessControlProvider";
+    private static final String CATEGORY_PLUGIN = "Plugin";
+    private static final String CATEGORY_TRUST_STORE = "TrustStore";
+    private static final String CATEGORY_KEY_STORE = "KeyStore";
+    private static final String CATEGORY_PORT = "Port";
+    private static final String CATEGORY_VIRTUAL_HOST_ALIAS = "VirtualHostAlias";
+    private static final String CATEGORY_GROUP_PROVIDER = "GroupProvider";
+    private static final String CATEGORY_GROUP = "Group";
+    private static final String CATEGORY_GROUP_MEMBER = "GroupMember";
+    private static final String CATEGORY_VIRTUAL_HOST_NODE = "VirtualHostNode";
+    private static final String CATEGORY_REMOTE_REPLICATION_NODE = "RemoteReplicationNode";
+    static final String CATEGORY_VIRTUAL_HOST = "VirtualHost";
+    private static final String CATEGORY_VIRTUAL_HOST_LOGGER = "VirtualHostLogger";
+    private static final String CATEGORY_VIRTUAL_HOST_LOG_INCLUSION_RULE = "VirtualHostLogInclusionRule";
+    private static final String CATEGORY_VIRTUAL_HOST_ACCESS_CONTROL_PROVIDER = "VirtualHostAccessControlProvider";
+    private static final String CATEGORY_EXCHANGE = "Exchange";
+    private static final String CATEGORY_QUEUE = "Queue";
+    private static final String CATEGORY_CONSUMER = "Consumer";
+    private static final String CATEGORY_CONNECTION = "Connection";
+    private static final String CATEGORY_SESSION = "Session";
+    private static final String CATEGORY_SYSTEM_CONFIG = "SystemConfig";
+    static final Map<String, String> SUPPORTED_CATEGORIES =
+            Collections.unmodifiableMap(new HashMap<String, String>()
+            {
+                {
+                    put(CATEGORY_BROKER_LOGGER, CATEGORY_BROKER);
+                    put(CATEGORY_BROKER_LOG_INCLUSION_RULE, CATEGORY_BROKER_LOGGER);
+                    put(CATEGORY_AUTHENTICATION_PROVIDER, CATEGORY_BROKER);
+                    put(CATEGORY_USER, CATEGORY_AUTHENTICATION_PROVIDER);
+                    put(CATEGORY_ACCESS_CONTROL_PROVIDER, CATEGORY_BROKER);
+                    put(CATEGORY_PLUGIN, CATEGORY_BROKER);
+                    put(CATEGORY_TRUST_STORE, CATEGORY_BROKER);
+                    put(CATEGORY_KEY_STORE, CATEGORY_BROKER);
+                    put(CATEGORY_PORT, CATEGORY_BROKER);
+                    put(CATEGORY_VIRTUAL_HOST_ALIAS, CATEGORY_PORT);
+                    put(CATEGORY_GROUP_PROVIDER, CATEGORY_BROKER);
+                    put(CATEGORY_GROUP, CATEGORY_GROUP_PROVIDER);
+                    put(CATEGORY_GROUP_MEMBER, CATEGORY_GROUP);
+                    put(CATEGORY_VIRTUAL_HOST_NODE, CATEGORY_BROKER);
+                    put(CATEGORY_REMOTE_REPLICATION_NODE, CATEGORY_VIRTUAL_HOST_NODE);
+                    put(CATEGORY_VIRTUAL_HOST, CATEGORY_VIRTUAL_HOST_NODE);
+                    put(CATEGORY_VIRTUAL_HOST_LOGGER, CATEGORY_VIRTUAL_HOST);
+                    put(CATEGORY_VIRTUAL_HOST_LOG_INCLUSION_RULE, CATEGORY_VIRTUAL_HOST_LOGGER);
+                    put(CATEGORY_VIRTUAL_HOST_ACCESS_CONTROL_PROVIDER, CATEGORY_VIRTUAL_HOST);
+                    put(CATEGORY_EXCHANGE, CATEGORY_VIRTUAL_HOST);
+                    put(CATEGORY_QUEUE, CATEGORY_VIRTUAL_HOST);
+                    put(CATEGORY_CONSUMER, CATEGORY_QUEUE);
+                    put(CATEGORY_CONNECTION, CATEGORY_VIRTUAL_HOST);
+                    put(CATEGORY_SESSION, CATEGORY_CONNECTION);
+                    put(CATEGORY_BROKER, CATEGORY_SYSTEM_CONFIG);
+                }
+            });
+
+    private static final Map<String, String> DEFAULT_TYPES = Collections.unmodifiableMap(new HashMap<String, String>()
+    {
+        {
+            put(CATEGORY_BROKER_LOGGER, "Broker");
+            put(CATEGORY_TRUST_STORE, "FileTrustStore");
+            put(CATEGORY_KEY_STORE, "FileKeyStore");
+            put(CATEGORY_GROUP, "ManagedGroup");
+            put(CATEGORY_GROUP_MEMBER, "ManagedGroupMember");
+            put(CATEGORY_VIRTUAL_HOST, "ProvidedStore");
+            put(CATEGORY_QUEUE, "standard");
+        }
+    });
+
+    @Override
+    public CategoryController createController(final String type,
+                                               final LegacyManagementController legacyManagementController)
+    {
+        if (SUPPORTED_CATEGORIES.containsKey(type))
+        {
+            if (CATEGORY_VIRTUAL_HOST.equals(type) || CATEGORY_BROKER.equals(type))
+            {
+                return new ContainerController(legacyManagementController,
+                                               type,
+                                               SUPPORTED_CATEGORIES.get(type),
+                                               DEFAULT_TYPES.get(type),
+                                               legacyManagementController.getTypeControllersByCategory(type));
+            }
+            else
+            {
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    SUPPORTED_CATEGORIES.get(type),
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException(String.format("Unsupported type '%s'", type));
+        }
+    }
+
+    @Override
+    public Set<String> getSupportedCategories()
+    {
+        return SUPPORTED_CATEGORIES.keySet();
+    }
+
+    @Override
+    public String getModelVersion()
+    {
+        return LegacyManagementControllerFactory.MODEL_VERSION;
+    }
+
+    @Override
+    public String getType()
+    {
+        return LegacyCategoryControllerFactory.class.getName();
+    }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/AbstractLegacyConfiguredObjectControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/AbstractLegacyConfiguredObjectControllerTest.java
new file mode 100644
index 0000000..8df9aea
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/AbstractLegacyConfiguredObjectControllerTest.java
@@ -0,0 +1,292 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementRequest;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.RequestType;
+import org.apache.qpid.server.management.plugin.ResponseType;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class AbstractLegacyConfiguredObjectControllerTest extends UnitTestBase
+{
+
+    private static final String TEST_VERSION = "testVersion";
+    private static final String TEST_CATEGORY = "testCategory";
+    private static final String TEST_CATEGORY_2 = "testCategory2";
+    private static final String TEST_TYPE = "testType";
+    private ManagementController _nextVersionManagementController;
+    private AbstractLegacyConfiguredObjectController _controller;
+    private CategoryController _categoryController2;
+    private ConfiguredObject<?> _root;
+    private TypeController _typeController;
+
+    @Before
+    public void setUp()
+    {
+        _nextVersionManagementController = mock(ManagementController.class);
+        _controller = new AbstractLegacyConfiguredObjectController(TEST_VERSION, _nextVersionManagementController)
+        {
+            @Override
+            public Object formatConfiguredObject(final Object content,
+                                                 final Map<String, List<String>> parameters,
+                                                 final boolean isSecureOrAllowedOnInsecureChannel)
+            {
+                return null;
+            }
+
+            @Override
+            protected Map<String, List<String>> convertQueryParameters(final Map<String, List<String>> parameters)
+            {
+                return parameters;
+            }
+        };
+        _root = mock(ConfiguredObject.class);
+        final CategoryControllerFactory categoryFactory = mock(CategoryControllerFactory.class);
+        final TypeControllerFactory typeFactory = mock(TypeControllerFactory.class);
+        final CategoryController categoryController = mock(CategoryController.class);
+        when(categoryController.getParentCategories()).thenReturn(new String[0]);
+        when(categoryController.getCategory()).thenReturn(TEST_CATEGORY);
+        _categoryController2 = mock(CategoryController.class);
+        when(_categoryController2.getCategory()).thenReturn(TEST_CATEGORY_2);
+        when(_categoryController2.getParentCategories()).thenReturn(new String[]{TEST_CATEGORY});
+
+        when(categoryFactory.getModelVersion()).thenReturn(TEST_VERSION);
+        when(categoryFactory.getSupportedCategories()).thenReturn(new HashSet<>(Arrays.asList(TEST_CATEGORY,
+                                                                                              TEST_CATEGORY_2)));
+        when(categoryFactory.createController(TEST_CATEGORY, _controller)).thenReturn(categoryController);
+        when(categoryFactory.createController(TEST_CATEGORY_2, _controller)).thenReturn(_categoryController2);
+        when(typeFactory.getCategory()).thenReturn(TEST_CATEGORY);
+        when(typeFactory.getModelVersion()).thenReturn(TEST_VERSION);
+        _typeController = mock(TypeController.class);
+        when(_typeController.getTypeName()).thenReturn(TEST_TYPE);
+        when(typeFactory.createController(_controller)).thenReturn(_typeController);
+        _controller.initialize(Collections.singleton(categoryFactory), Collections.singleton(typeFactory));
+    }
+
+    @Test
+    public void getVersion()
+    {
+        assertThat(_controller.getVersion(), is(equalTo(TEST_VERSION)));
+    }
+
+    @Test
+    public void getCategories()
+    {
+        Collection<String> categories = _controller.getCategories();
+        Set<String> expected = new HashSet<>(Arrays.asList(TEST_CATEGORY, TEST_CATEGORY_2));
+        assertThat(new HashSet<>(categories), is(equalTo(expected)));
+    }
+
+    @Test
+    public void getCategoryMapping()
+    {
+        assertThat(_controller.getCategoryMapping(TEST_CATEGORY), is(equalTo(
+                String.format("/api/v%s/%s/", TEST_VERSION, TEST_CATEGORY.toLowerCase()))));
+    }
+
+    @Test
+    public void getCategory()
+    {
+        final ConfiguredObject<?> managementObject = mock(ConfiguredObject.class);
+        when(_nextVersionManagementController.getCategory(managementObject)).thenReturn(TEST_CATEGORY);
+        String category = _controller.getCategory(managementObject);
+        assertThat(category, is(equalTo(TEST_CATEGORY)));
+    }
+
+    @Test
+    public void getCategoryHierarchy()
+    {
+        when(_nextVersionManagementController.getCategory(_root)).thenReturn(TEST_CATEGORY);
+        final List<String> hierarchy = _controller.getCategoryHierarchy(_root, TEST_CATEGORY_2);
+
+        final Set<String> expected = Collections.singleton(TEST_CATEGORY_2);
+        assertThat(new HashSet<>(hierarchy), is(equalTo(expected)));
+    }
+
+    @Test
+    public void getNextVersionManagementController()
+    {
+        assertThat(_controller.getNextVersionManagementController(), is(equalTo(_nextVersionManagementController)));
+    }
+
+    @Test
+    public void createOrUpdate()
+    {
+        final List<String> path = Collections.singletonList("test");
+        final Map<String, Object> attributes = Collections.singletonMap("name", "test");
+        final LegacyConfiguredObject object = mock(LegacyConfiguredObject.class);
+        when(_categoryController2.createOrUpdate(_root, path, attributes, false)).thenReturn(object);
+        final LegacyConfiguredObject result =
+                _controller.createOrUpdate(_root, TEST_CATEGORY_2, path, attributes, false);
+        assertThat(result, is(equalTo(object)));
+    }
+
+    @Test
+    public void get()
+    {
+        final List<String> path = Collections.singletonList("test");
+        final LegacyConfiguredObject object = mock(LegacyConfiguredObject.class);
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("name", Collections.singletonList("test"));
+        when(_categoryController2.get(_root, path, parameters)).thenReturn(object);
+        final Object result = _controller.get(_root, TEST_CATEGORY_2, path, parameters);
+        assertThat(result, is(equalTo(object)));
+    }
+
+    @Test
+    public void delete()
+    {
+        final List<String> path = Collections.singletonList("test");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("name", Collections.singletonList("test"));
+        _controller.delete(_root, TEST_CATEGORY_2, path, parameters);
+        verify(_categoryController2).delete(_root, path, parameters);
+    }
+
+    @Test
+    public void invoke()
+    {
+        final List<String> path = Collections.singletonList("test");
+        final Map<String, Object> parameters = Collections.singletonMap("name", "test");
+        final Object operationResult = mock(Object.class);
+
+        final String operationName = "testOperation";
+
+        final ManagementResponse managementResponse =
+                new ControllerManagementResponse(ResponseType.DATA, operationResult);
+        when(_categoryController2.invoke(_root, path, operationName, parameters, true, true)).thenReturn(
+                managementResponse);
+        final ManagementResponse result =
+                _controller.invoke(_root, TEST_CATEGORY_2, path, operationName, parameters, true, true);
+
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getResponseCode(), is(equalTo(200)));
+        assertThat(result.getBody(), is(equalTo(operationResult)));
+        verify(_categoryController2).invoke(_root, path, operationName, parameters, true, true);
+    }
+
+    @Test
+    public void getPreferences()
+    {
+        final List<String> path = Arrays.asList("test", "preferences");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("name", Collections.singletonList("test"));
+        final Object prefs = mock(Object.class);
+        when(_categoryController2.getPreferences(_root, path, parameters)).thenReturn(prefs);
+        final Object preferences = _controller.getPreferences(_root, TEST_CATEGORY_2, path, parameters);
+        assertThat(preferences, is(equalTo(prefs)));
+        verify(_categoryController2).getPreferences(_root, path, parameters);
+    }
+
+    @Test
+    public void setPreferences()
+    {
+        final List<String> path = Arrays.asList("test", "preferences");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("name", Collections.singletonList("test"));
+        final Object prefs = mock(Object.class);
+        _controller.setPreferences(_root, TEST_CATEGORY_2, path, prefs, parameters, true);
+        verify(_categoryController2).setPreferences(_root, path, prefs, parameters, true);
+    }
+
+    @Test
+    public void deletePreferences()
+    {
+        final List<String> path = Arrays.asList("test", "preferences");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("name", Collections.singletonList("test"));
+        _controller.deletePreferences(_root, TEST_CATEGORY_2, path, parameters);
+        verify(_categoryController2).deletePreferences(_root, path, parameters);
+    }
+
+    @Test
+    public void getCategoryController()
+    {
+        assertThat(_controller.getCategoryController(TEST_CATEGORY_2), is(equalTo(_categoryController2)));
+    }
+
+    @Test
+    public void getTypeControllersByCategory()
+    {
+        final Set<TypeController> typeControllers = _controller.getTypeControllersByCategory(TEST_CATEGORY);
+        assertThat(typeControllers, is(equalTo(Collections.singleton(_typeController))));
+    }
+
+    @Test
+    public void getChildrenCategories()
+    {
+        final Collection<String> childrenCategories = _controller.getChildrenCategories(TEST_CATEGORY);
+        assertThat(new HashSet<>(childrenCategories), is(equalTo(Collections.singleton(TEST_CATEGORY_2))));
+    }
+
+    @Test
+    public void getParentTypes()
+    {
+        final Collection<String> childrenCategories = _controller.getParentTypes(TEST_CATEGORY_2);
+        assertThat(new HashSet<>(childrenCategories), is(equalTo(Collections.singleton(TEST_CATEGORY))));
+    }
+
+    @Test
+    public void getNextVersionLegacyConfiguredObjectConverter()
+    {
+        assertThat(_controller.getNextVersionManagementController(), is(equalTo(_nextVersionManagementController)));
+    }
+
+    @Test
+    public void getRequestType()
+    {
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("name", Collections.singletonList("test"));
+        final ManagementRequest managementRequest = mock(ManagementRequest.class);
+        doReturn(_root).when(managementRequest).getRoot();
+        when(managementRequest.getMethod()).thenReturn("GET");
+        when(managementRequest.getParameters()).thenReturn(parameters);
+        when(managementRequest.getPath()).thenReturn(Collections.singletonList("test"));
+        when(managementRequest.getCategory()).thenReturn(TEST_CATEGORY_2);
+        when(_controller.getCategory(_root)).thenReturn(TEST_CATEGORY);
+
+        final RequestType requestType = _controller.getRequestType(managementRequest);
+        assertThat(requestType, is(equalTo(RequestType.MODEL_OBJECT)));
+    }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/GenericCategoryControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/GenericCategoryControllerTest.java
new file mode 100644
index 0000000..1f336ae
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/GenericCategoryControllerTest.java
@@ -0,0 +1,247 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.controller;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class GenericCategoryControllerTest extends UnitTestBase
+{
+    private static final String TEST_CATEGORY = "testCategory";
+    private static final String DEFAULT_TYPE = "defaultType";
+    private LegacyManagementController _managementController;
+    private LegacyManagementController _nextVersionManagementController;
+    private GenericCategoryController _controller;
+    private ConfiguredObject _root;
+    private TypeController _typeController;
+    private LegacyConfiguredObject _converted;
+
+    @Before
+    public void setUp()
+    {
+        _managementController = mock(LegacyManagementController.class);
+        _nextVersionManagementController = mock(LegacyManagementController.class);
+        _typeController = mock(TypeController.class);
+        when(_typeController.getTypeName()).thenReturn(DEFAULT_TYPE);
+        when(_typeController.getNextVersionTypeName()).thenReturn(DEFAULT_TYPE);
+        _converted = mock(LegacyConfiguredObject.class);
+        _controller = new GenericCategoryController(_managementController,
+                                                    _nextVersionManagementController,
+                                                    TEST_CATEGORY,
+                                                    DEFAULT_TYPE,
+                                                    Collections.singleton(_typeController))
+        {
+            @Override
+            protected LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+            {
+                return _converted;
+            }
+
+            @Override
+            public String[] getParentCategories()
+            {
+                return new String[0];
+            }
+        };
+        _root = mock(ConfiguredObject.class);
+    }
+
+    @Test
+    public void getCategory()
+    {
+        assertThat(_controller.getCategory(), is(equalTo(TEST_CATEGORY)));
+    }
+
+    @Test
+    public void getDefaultType()
+    {
+        assertThat(_controller.getDefaultType(), is(equalTo(DEFAULT_TYPE)));
+    }
+
+    @Test
+    public void getManagementController()
+    {
+        assertThat(_controller.getManagementController(), is(equalTo(_managementController)));
+    }
+
+    @Test
+    public void get()
+    {
+        final List<String> path = Arrays.asList("test1", "test2");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("testParam", Collections.singletonList("testValue"));
+        final LegacyConfiguredObject result = mock(LegacyConfiguredObject.class);
+        when(result.getAttribute(LegacyConfiguredObject.TYPE)).thenReturn(DEFAULT_TYPE);
+        final LegacyConfiguredObject convertedResult = mock(LegacyConfiguredObject.class);
+        when(_nextVersionManagementController.get(_root, TEST_CATEGORY, path, parameters)).thenReturn(result);
+        when(_typeController.convertFromNextVersion(result)).thenReturn(convertedResult);
+        final Object readResult = _controller.get(_root, path, parameters);
+        assertThat(readResult, is(equalTo(convertedResult)));
+        verify(_nextVersionManagementController).get(_root, TEST_CATEGORY, path, parameters);
+    }
+
+    @Test
+    public void createOrUpdate()
+    {
+        final List<String> path = Arrays.asList("test1", "test2");
+        final Map<String, Object> attributes = Collections.singletonMap("test", "testValue");
+        final LegacyConfiguredObject result = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject convertedResult = mock(LegacyConfiguredObject.class);
+        when(result.getAttribute(LegacyConfiguredObject.TYPE)).thenReturn(DEFAULT_TYPE);
+
+        when(_typeController.convertAttributesToNextVersion(_root, path, attributes)).thenReturn(attributes);
+        when(_typeController.convertFromNextVersion(result)).thenReturn(convertedResult);
+        when(_nextVersionManagementController.createOrUpdate(_root, TEST_CATEGORY, path, attributes, false))
+                .thenReturn(result);
+        when(_typeController.convertFromNextVersion(result)).thenReturn(convertedResult);
+        final Object createResult = _controller.createOrUpdate(_root, path, attributes, false);
+        assertThat(createResult, is(equalTo(convertedResult)));
+        verify(_nextVersionManagementController).createOrUpdate(_root, TEST_CATEGORY, path, attributes, false);
+    }
+
+    @Test
+    public void delete()
+    {
+        final List<String> path = Arrays.asList("test1", "test2");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("testParam", Collections.singletonList("testValue"));
+
+        final int result = 1;
+        when(_nextVersionManagementController.delete(_root, TEST_CATEGORY, path, parameters)).thenReturn(result);
+        final Object deleteResult = _controller.delete(_root, path, parameters);
+        assertThat(deleteResult, is(equalTo(result)));
+        verify(_nextVersionManagementController).delete(_root, TEST_CATEGORY, path, parameters);
+    }
+
+    @Test
+    public void invoke()
+    {
+        final List<String> path = Arrays.asList("test1", "test2");
+        final String operationName = "testOperation";
+        final Map<String, Object> operationParameters = Collections.singletonMap("testParam", "testValue");
+
+        final LegacyConfiguredObject result = mock(LegacyConfiguredObject.class);
+        when(result.getAttribute(LegacyConfiguredObject.TYPE)).thenReturn(DEFAULT_TYPE);
+        final LegacyConfiguredObject convertedResult = mock(LegacyConfiguredObject.class);
+        when(_nextVersionManagementController.get(eq(_root),
+                                                  eq(TEST_CATEGORY),
+                                                  eq(path),
+                                                  eq(Collections.emptyMap()))).thenReturn(result);
+        when(_typeController.convertFromNextVersion(result)).thenReturn(convertedResult);
+
+
+        final Object operationValue = "testValue";
+        final ManagementResponse operationResult = new ControllerManagementResponse(ResponseType.DATA, operationValue);
+        when(convertedResult.invoke(operationName, operationParameters, true)).thenReturn(operationResult);
+        final ManagementResponse response =
+                _controller.invoke(_root, path, operationName, operationParameters, true, true);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        assertThat(response.getBody(), is(equalTo(operationValue)));
+        verify(_nextVersionManagementController).get(_root, TEST_CATEGORY, path, Collections.emptyMap());
+        verify(convertedResult).invoke(operationName, operationParameters, true);
+    }
+
+    @Test
+    public void getPreferences()
+    {
+        final List<String> path = Arrays.asList("test1", "test2");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("testParam", Collections.singletonList("testValue"));
+
+        final Object result = mock(Object.class);
+        when(_nextVersionManagementController.getPreferences(_root,
+                                                             TEST_CATEGORY,
+                                                             path,
+                                                             parameters)).thenReturn(result);
+
+        final Object preferences = _controller.getPreferences(_root, path, parameters);
+        assertThat(preferences, is(equalTo(result)));
+        verify(_nextVersionManagementController).getPreferences(_root, TEST_CATEGORY, path, parameters);
+    }
+
+    @Test
+    public void setPreferences()
+    {
+        final List<String> path = Arrays.asList("test1", "test2");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("testParam", Collections.singletonList("testValue"));
+
+        final Object preferences = mock(Object.class);
+        _controller.setPreferences(_root, path, preferences, parameters, true);
+
+        verify(_nextVersionManagementController).setPreferences(_root,
+                                                                TEST_CATEGORY,
+                                                                path,
+                                                                preferences,
+                                                                parameters,
+                                                                true);
+    }
+
+    @Test
+    public void deletePreferences()
+    {
+        final List<String> path = Arrays.asList("test1", "test2");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("testParam", Collections.singletonList("testValue"));
+
+        _controller.deletePreferences(_root, path, parameters);
+
+        verify(_nextVersionManagementController).deletePreferences(_root, TEST_CATEGORY, path, parameters);
+    }
+
+    @Test
+    public void getNextVersionManagementController()
+    {
+        assertThat(_controller.getNextVersionManagementController(), is(equalTo(_nextVersionManagementController)));
+    }
+
+    @Test
+    public void convertAttributesToNextVersion()
+    {
+        final List<String> path = Arrays.asList("test1", "test2");
+        final Map<String, Object> attributes = Collections.singletonMap("testParam", "testValue");
+        final Map<String, Object> convertedAttributes = Collections.singletonMap("testParam", "testValue2");
+
+        when(_typeController.convertAttributesToNextVersion(_root, path, attributes)).thenReturn(convertedAttributes);
+        final Map<String, Object> converted = _controller.convertAttributesToNextVersion(_root, path, attributes);
+        assertThat(converted, is(equalTo(convertedAttributes)));
+    }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/GenericLegacyConfiguredObjectTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/GenericLegacyConfiguredObjectTest.java
new file mode 100644
index 0000000..b55a1f2
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/GenericLegacyConfiguredObjectTest.java
@@ -0,0 +1,212 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class GenericLegacyConfiguredObjectTest extends UnitTestBase
+{
+
+    private GenericLegacyConfiguredObject _object;
+    private LegacyConfiguredObject _nextVersionLegacyConfiguredObject;
+    private static final String CATEGORY = "testCategory";
+    private LegacyManagementController _managementController;
+
+    @Before
+    public void setUp()
+    {
+        _managementController = mock(LegacyManagementController.class);
+        _nextVersionLegacyConfiguredObject = mock(LegacyConfiguredObject.class);
+
+        _object = new GenericLegacyConfiguredObject(_managementController,
+                                                    _nextVersionLegacyConfiguredObject,
+                                                    CATEGORY);
+    }
+
+    @Test
+    public void getAttributeNames()
+    {
+        final Collection<String> attributeNames = Arrays.asList("foo", "bar", "test");
+        when(_nextVersionLegacyConfiguredObject.getAttributeNames()).thenReturn(attributeNames);
+        Collection<String> names = _object.getAttributeNames();
+
+        assertThat(names, is(equalTo(attributeNames)));
+        verify(_nextVersionLegacyConfiguredObject).getAttributeNames();
+    }
+
+    @Test
+    public void getAttribute()
+    {
+        final String attributeName = "name";
+        final String attributeValue = "test";
+
+        when(_nextVersionLegacyConfiguredObject.getAttribute(attributeName)).thenReturn(attributeValue);
+        final Object value = _object.getAttribute(attributeName);
+
+        assertThat(value, is(equalTo(attributeValue)));
+        verify(_nextVersionLegacyConfiguredObject).getAttribute(attributeName);
+    }
+
+    @Test
+    public void getActualAttribute()
+    {
+        final String attributeName = "name";
+        final String attributeValue = "test";
+
+        when(_nextVersionLegacyConfiguredObject.getActualAttribute(attributeName)).thenReturn(attributeValue);
+        final Object value = _object.getActualAttribute(attributeName);
+
+        assertThat(value, is(equalTo(attributeValue)));
+        verify(_nextVersionLegacyConfiguredObject).getActualAttribute(attributeName);
+    }
+
+    @Test
+    public void getChildren()
+    {
+        final String childrenCategory = "testChildrenCategory";
+
+        final LegacyConfiguredObject child = mock(LegacyConfiguredObject.class);
+        final Collection<LegacyConfiguredObject> children = Collections.singleton(child);
+        when(_nextVersionLegacyConfiguredObject.getChildren(childrenCategory)).thenReturn(children);
+        final LegacyConfiguredObject converted = mock(LegacyConfiguredObject.class);
+        when(_managementController.convertFromNextVersion(child)).thenReturn(converted);
+        final Collection<LegacyConfiguredObject> value = _object.getChildren(childrenCategory);
+
+        assertThat(value.size(), is(equalTo(1)));
+        final LegacyConfiguredObject convertedChild = value.iterator().next();
+        assertThat(convertedChild, is(equalTo(converted)));
+        verify(_nextVersionLegacyConfiguredObject).getChildren(childrenCategory);
+        verify(_managementController).convertFromNextVersion(child);
+    }
+
+    @Test
+    public void getCategory()
+    {
+        assertThat(_object.getCategory(), is(equalTo(CATEGORY)));
+    }
+
+    @Test
+    public void invoke()
+    {
+        final String operationName = "testOperation";
+        final Map<String, Object> operationArguments = Collections.singletonMap("arg", "argValue");
+        final String operationResult = "testOperationResult";
+        final ControllerManagementResponse managementResponse = new ControllerManagementResponse(
+                ResponseType.DATA, operationResult);
+        when(_nextVersionLegacyConfiguredObject.invoke(operationName,
+                                                       operationArguments,
+                                                       true)).thenReturn(managementResponse);
+
+        final ManagementResponse result =
+                _object.invoke(operationName, operationArguments, true);
+
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getResponseCode(), is(equalTo(200)));
+        assertThat(result.getBody(), is(equalTo(operationResult)));
+    }
+
+    @Test
+    public void getNextVersionConfiguredObject()
+    {
+        assertThat(_object.getNextVersionConfiguredObject(), is(equalTo(_nextVersionLegacyConfiguredObject)));
+    }
+
+    @Test
+    public void getParent()
+    {
+        final String parentCategory = "testParentCategory";
+        final LegacyConfiguredObject nextVersionParent = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject nextVersionParentConverted = mock(LegacyConfiguredObject.class);
+        when(_nextVersionLegacyConfiguredObject.getParent(parentCategory)).thenReturn(nextVersionParent);
+        when(_managementController.convertFromNextVersion(nextVersionParent)).thenReturn(
+                nextVersionParentConverted);
+        final LegacyConfiguredObject parent = _object.getParent(parentCategory);
+        assertThat(parent, is(equalTo(nextVersionParentConverted)));
+        verify(_nextVersionLegacyConfiguredObject).getParent(parentCategory);
+    }
+
+    @Test
+    public void isSecureAttribute()
+    {
+        final String attributeName = "testAttribute";
+        when(_nextVersionLegacyConfiguredObject.isSecureAttribute(attributeName)).thenReturn(true);
+        assertThat(_object.isSecureAttribute(attributeName), is(equalTo(true)));
+        verify(_nextVersionLegacyConfiguredObject).isSecureAttribute(attributeName);
+    }
+
+    @Test
+    public void isOversizedAttribute()
+    {
+        final String attributeName = "testAttribute";
+        when(_nextVersionLegacyConfiguredObject.isOversizedAttribute(attributeName)).thenReturn(true);
+        assertThat(_object.isOversizedAttribute(attributeName), is(equalTo(true)));
+        verify(_nextVersionLegacyConfiguredObject).isOversizedAttribute(attributeName);
+    }
+
+    @Test
+    public void getContextValue()
+    {
+        final String contextName = "testContext";
+        final String contextValue = "testValue";
+        when(_nextVersionLegacyConfiguredObject.getContextValue(contextName)).thenReturn(contextValue);
+        assertThat(_object.getContextValue(contextName), is(equalTo(contextValue)));
+        verify(_nextVersionLegacyConfiguredObject).getContextValue(contextName);
+    }
+
+    @Test
+    public void getStatistics()
+    {
+        Map<String, Object> stats = Collections.singletonMap("testStat", "statValue");
+        when(_nextVersionLegacyConfiguredObject.getStatistics()).thenReturn(stats);
+        assertThat(_object.getStatistics(), is(equalTo(stats)));
+        verify(_nextVersionLegacyConfiguredObject).getStatistics();
+    }
+
+    @Test
+    public void getManagementController()
+    {
+        assertThat(_object.getManagementController(), is(equalTo(_managementController)));
+    }
+
+    @Test
+    public void getNextVersionLegacyConfiguredObject()
+    {
+        assertThat(_object.getNextVersionConfiguredObject(), is(equalTo(_nextVersionLegacyConfiguredObject)));
+    }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerAdapterTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerAdapterTest.java
new file mode 100644
index 0000000..aede511
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerAdapterTest.java
@@ -0,0 +1,395 @@
+/*
+ *
+ * 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.management.plugin.controller.latest;
+
+import static org.apache.qpid.server.management.plugin.HttpManagementConfiguration.PREFERENCE_OPERTAION_TIMEOUT_CONTEXT_NAME;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementRequest;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.BrokerModel;
+import org.apache.qpid.server.model.BrokerTestHelper;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.VirtualHostNode;
+import org.apache.qpid.server.virtualhost.QueueManagingVirtualHost;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class LatestManagementControllerAdapterTest extends UnitTestBase
+{
+    private LatestManagementControllerAdapter _adapter;
+
+    @Before
+    public void setUp()
+    {
+        final HttpManagementConfiguration<?> httpManagement = mock(HttpManagementConfiguration.class);
+        when(httpManagement.getContextValue(Long.class, PREFERENCE_OPERTAION_TIMEOUT_CONTEXT_NAME)).thenReturn(1000L);
+        final ManagementController managementController = new LatestManagementController(httpManagement);
+        when(managementController.getVersion()).thenReturn(BrokerModel.MODEL_VERSION);
+        _adapter = new LatestManagementControllerAdapter(managementController);
+    }
+
+    @Test
+    public void getVersion()
+    {
+        assertThat(_adapter.getVersion(), is(equalTo(BrokerModel.MODEL_VERSION)));
+    }
+
+    @Test
+    public void getCategories()
+    {
+        assertThat(_adapter.getCategories(), is(equalTo(BrokerModel.getInstance()
+                                                                   .getSupportedCategories()
+                                                                   .stream()
+                                                                   .map(Class::getSimpleName)
+                                                                   .collect(Collectors.toSet()))));
+    }
+
+    @Test
+    public void getCategoryMapping()
+    {
+        assertThat(_adapter.getCategoryMapping("foo"),
+                   is(equalTo(String.format("/api/v%s/%s/", BrokerModel.MODEL_VERSION, "foo"))));
+    }
+
+    @Test
+    public void getCategory()
+    {
+        final ConfiguredObject<?> object = mock(ConfiguredObject.class);
+        doReturn(Broker.class).when(object).getCategoryClass();
+        assertThat(_adapter.getCategory(object), is(equalTo(Broker.class.getSimpleName())));
+    }
+
+    @Test
+    public void getCategoryHierarchy()
+    {
+        final Broker<?> object = BrokerTestHelper.createBrokerMock();
+        final Collection<String> expected = Arrays.asList("VirtualHostNode", "VirtualHost", "Queue");
+        assertThat(_adapter.getCategoryHierarchy(object, "Queue"), is(equalTo(expected)));
+    }
+
+    @Test
+    public void handleGet() throws Exception
+    {
+        final String hostName = "test";
+        final String queueName = "foo";
+
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        virtualHost.createChild(Queue.class, Collections.singletonMap(Queue.NAME, queueName));
+
+        final String nodeName = virtualHost.getParent().getName();
+        final ManagementRequest request = mockManagementRequest(virtualHost.getBroker(),
+                                                                "GET",
+                                                                "queue",
+                                                                Arrays.asList(nodeName, hostName),
+                                                                Collections.singletonMap("name",
+                                                                                         Collections.singletonList("foo")));
+
+        final ManagementResponse response = _adapter.handleGet(request);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        assertThat(response.getBody(), is(notNullValue()));
+        assertThat(response.getBody(), is(instanceOf(Collection.class)));
+
+        final Collection data = (Collection) response.getBody();
+        assertThat(data.size(), is(equalTo(1)));
+
+        final Object object = data.iterator().next();
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(LegacyConfiguredObject.class)));
+        assertThat(((LegacyConfiguredObject) object).getAttribute(LegacyConfiguredObject.NAME), is(equalTo("foo")));
+    }
+
+
+    @Test
+    public void handlePut() throws Exception
+    {
+        final String hostName = "test";
+        final String queueName = "foo";
+
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        final String nodeName = virtualHost.getParent().getName();
+        final Broker root = virtualHost.getBroker();
+
+        final ManagementRequest request =
+                mockManagementRequest(root, "PUT", "queue", Arrays.asList(nodeName, hostName), Collections.emptyMap());
+
+        when(request.getBody(LinkedHashMap.class)).thenReturn(new LinkedHashMap<String, Object>(Collections.singletonMap(
+                "name",
+                queueName)));
+        when(request.getRequestURL()).thenReturn("test");
+
+        final ManagementResponse response = _adapter.handlePut(request);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(201)));
+        assertThat(response.getBody(), is(notNullValue()));
+        assertThat(response.getBody(), is(instanceOf(LegacyConfiguredObject.class)));
+
+        final LegacyConfiguredObject object = (LegacyConfiguredObject) response.getBody();
+        assertThat(object.getAttribute(LegacyConfiguredObject.NAME), is(equalTo("foo")));
+    }
+
+    @Test
+    public void handlePost() throws Exception
+    {
+        final String hostName = "test";
+        final String queueName = "foo";
+
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        final String nodeName = virtualHost.getParent().getName();
+        final Broker root = virtualHost.getBroker();
+
+        final ManagementRequest request =
+                mockManagementRequest(root, "POST", "queue", Arrays.asList(nodeName, hostName), Collections.emptyMap());
+
+        when(request.getBody(LinkedHashMap.class)).thenReturn(new LinkedHashMap<String, Object>(Collections.singletonMap(
+                "name",
+                queueName)));
+        when(request.getRequestURL()).thenReturn("test");
+
+        final ManagementResponse response = _adapter.handlePut(request);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(201)));
+        assertThat(response.getBody(), is(notNullValue()));
+        assertThat(response.getBody(), is(instanceOf(LegacyConfiguredObject.class)));
+
+        final LegacyConfiguredObject object = (LegacyConfiguredObject) response.getBody();
+        assertThat(object.getAttribute(LegacyConfiguredObject.NAME), is(equalTo("foo")));
+    }
+
+    @Test
+    public void handleDelete() throws Exception
+    {
+        final String hostName = "test";
+        final String queueName = "foo";
+
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        virtualHost.createChild(Queue.class, Collections.singletonMap(Queue.NAME, queueName));
+        assertThat(virtualHost.getQueueCount(), is(equalTo(1L)));
+
+        final String nodeName = virtualHost.getParent().getName();
+        final ManagementRequest request = mockManagementRequest(virtualHost.getBroker(),
+                                                                "DELETE",
+                                                                "queue",
+                                                                Arrays.asList(nodeName, hostName),
+                                                                Collections.singletonMap("name",
+                                                                                         Collections.singletonList("foo")));
+        final ManagementResponse response = _adapter.handleDelete(request);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        assertThat(virtualHost.getQueueCount(), is(equalTo(0L)));
+    }
+
+    @Test
+    public void get() throws Exception
+    {
+        final String hostName = "test";
+        final String queueName = "foo";
+
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        virtualHost.createChild(Queue.class, Collections.singletonMap(Queue.NAME, queueName));
+
+        final String nodeName = virtualHost.getParent().getName();
+
+        final Object response = _adapter.get(virtualHost.getBroker(), "queue", Arrays.asList(nodeName, hostName),
+                                             Collections.singletonMap("name",
+                                                                      Collections.singletonList("foo")));
+        assertThat(response, is(instanceOf(Collection.class)));
+
+        final Collection data = (Collection) response;
+        assertThat(data.size(), is(equalTo(1)));
+
+        final Object object = data.iterator().next();
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(LegacyConfiguredObject.class)));
+        assertThat(((LegacyConfiguredObject) object).getAttribute(LegacyConfiguredObject.NAME), is(equalTo("foo")));
+    }
+
+    @Test
+    public void createOrUpdate() throws Exception
+    {
+        final String hostName = "test";
+        final String queueName = "foo";
+
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        final String nodeName = virtualHost.getParent().getName();
+        final Broker root = virtualHost.getBroker();
+
+        final ManagementRequest request =
+                mockManagementRequest(root, "POST", "queue", Arrays.asList(nodeName, hostName), Collections.emptyMap());
+
+        when(request.getBody(LinkedHashMap.class)).thenReturn(new LinkedHashMap<String, Object>(Collections.singletonMap(
+                "name",
+                queueName)));
+        when(request.getRequestURL()).thenReturn("test");
+
+        final Object response = _adapter.createOrUpdate(virtualHost.getBroker(),
+                                                        "queue",
+                                                        Arrays.asList(nodeName, hostName),
+                                                        Collections.singletonMap("name", queueName),
+                                                        true);
+        assertThat(response, is(instanceOf(LegacyConfiguredObject.class)));
+
+        final LegacyConfiguredObject object = (LegacyConfiguredObject) response;
+        assertThat(object.getAttribute(LegacyConfiguredObject.NAME), is(equalTo(queueName)));
+    }
+
+    @Test
+    public void delete() throws Exception
+    {
+        final String hostName = "test";
+        final String queueName = "foo";
+
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        virtualHost.createChild(Queue.class, Collections.singletonMap(Queue.NAME, queueName));
+        assertThat(virtualHost.getQueueCount(), is(equalTo(1L)));
+
+        final String nodeName = virtualHost.getParent().getName();
+
+        _adapter.delete(virtualHost.getBroker(),
+                        "queue",
+                        Arrays.asList(nodeName, hostName, queueName),
+                        Collections.emptyMap());
+
+        assertThat(virtualHost.getQueueCount(), is(equalTo(0L)));
+    }
+
+    @Test
+    public void invoke() throws Exception
+    {
+        final String hostName = "test";
+        final String queueName = "foo";
+
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        Queue queue = virtualHost.createChild(Queue.class, Collections.singletonMap(Queue.NAME, queueName));
+        assertThat(virtualHost.getQueueCount(), is(equalTo(1L)));
+
+        List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName);
+
+        Map<String, Object> message = new HashMap<>();
+        message.put("address", "foo");
+        message.put("persistent", "false");
+        message.put("content", "Test Content");
+        message.put("mimeType", "text/plain");
+        ManagementResponse response = _adapter.invoke(virtualHost.getBroker(),
+                                                         "virtualhost",
+                                                         path,
+                                                         "publishMessage",
+                                                         Collections.singletonMap("message", message),
+                                                         true,
+                                                         true);
+
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        Object body = response.getBody();
+        assertThat(body, is(instanceOf(Number.class)));
+        assertThat(((Number) body).intValue(), is(equalTo(1)));
+        assertThat(queue.getQueueDepthMessages(), is(equalTo(1)));
+    }
+
+    @Test
+    public void formatConfiguredObject() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createTestVirtualHost(hostName);
+        final String queueName = "foo";
+        virtualHost.createChild(Queue.class, Collections.singletonMap(Queue.NAME, queueName));
+        assertThat(virtualHost.getQueueCount(), is(equalTo(1L)));
+
+        final Object formatted = _adapter.formatConfiguredObject(virtualHost,
+                                                                    Collections.singletonMap("depth",
+                                                                                             Collections.singletonList(
+                                                                                                     "1")),
+                                                                    true);
+        assertThat(formatted, is(notNullValue()));
+        assertThat(formatted, is(instanceOf(Map.class)));
+
+        final Map<?, ?> data = (Map<?, ?>) formatted;
+        assertThat(data.get(VirtualHost.NAME), is(equalTo(hostName)));
+        final Object queues = data.get("queues");
+        assertThat(queues, is(notNullValue()));
+        assertThat(queues, is(instanceOf(Collection.class)));
+
+        final Collection<?> queueCollection = (Collection<?>) queues;
+
+        assertThat(queueCollection.size(), is(equalTo(1)));
+        final Iterator<?> iterator = queueCollection.iterator();
+        final Object queue1 = iterator.next();
+
+        assertThat(queue1, is(instanceOf(Map.class)));
+
+        final Map<?, ?> queueMap1 = (Map<?, ?>) queue1;
+
+        assertThat(queueMap1.get(Queue.NAME), is(equalTo("foo")));
+    }
+
+
+    private QueueManagingVirtualHost<?> createTestVirtualHost(final String hostName) throws Exception
+    {
+        final QueueManagingVirtualHost<?> virtualHost = BrokerTestHelper.createVirtualHost(hostName, this);
+        final Broker root = virtualHost.getBroker();
+        final ConfiguredObject<?> virtualHostNode = virtualHost.getParent();
+        when(root.getChildren(VirtualHostNode.class)).thenReturn(Collections.singletonList(virtualHostNode));
+        when(virtualHostNode.getChildren(VirtualHost.class)).thenReturn(Collections.singletonList(virtualHost));
+        when(virtualHostNode.getChildByName(VirtualHost.class, hostName)).thenReturn(virtualHost);
+        return virtualHost;
+    }
+
+    private ManagementRequest mockManagementRequest(ConfiguredObject<?> root,
+                                                    String method,
+                                                    String category,
+                                                    List<String> path,
+                                                    Map<String, List<String>> parameters)
+    {
+        final ManagementRequest request = mock(ManagementRequest.class);
+        when(request.getCategory()).thenReturn(category);
+        doReturn(root).when(request).getRoot();
+        when(request.getPath()).thenReturn(path);
+        when(request.getParameters()).thenReturn(parameters);
+        when(request.getMethod()).thenReturn(method);
+        return request;
+    }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementControllerTest.java
new file mode 100644
index 0000000..8c7c378
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/LegacyManagementControllerTest.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * 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.management.plugin.controller.v7_0;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class LegacyManagementControllerTest extends UnitTestBase
+{
+    private LegacyManagementController _controller;
+
+    @Before
+    public void setUp()
+    {
+        final ManagementController nextVersionManagementController = mock(ManagementController.class);
+        _controller = new LegacyManagementController(nextVersionManagementController);
+        _controller.initialize();
+    }
+
+    @Test
+    public void convertQueryParameters()
+    {
+        final Map<String, List<String>> parameters = Collections.singletonMap("depth", Collections.singletonList("1"));
+        final Map<String, List<String>> converted = _controller.convertQueryParameters(parameters);
+        assertThat(converted, is(equalTo(parameters)));
+    }
+
+    @Test
+    public void formatConfiguredObject()
+    {
+        final String objectName = "test-object";
+        final String hostName = "test-vhn";
+        final Map<String, List<String>> parameters = Collections.singletonMap("depth", Collections.singletonList("1"));
+        final LegacyConfiguredObject object = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject vhn = mock(LegacyConfiguredObject.class);
+        when(object.getAttributeNames()).thenReturn(Arrays.asList(LegacyConfiguredObject.NAME,
+                                                                  LegacyConfiguredObject.TYPE));
+        when(object.getAttribute(LegacyConfiguredObject.NAME)).thenReturn(objectName);
+        when(object.getAttribute(LegacyConfiguredObject.TYPE)).thenReturn("Broker");
+        when(object.getCategory()).thenReturn("Broker");
+        when(object.getChildren("VirtualHostNode")).thenReturn(Collections.singletonList(vhn));
+        when(vhn.getAttributeNames()).thenReturn(Arrays.asList(LegacyConfiguredObject.NAME,
+                                                                  LegacyConfiguredObject.TYPE));
+        when(vhn.getAttribute(LegacyConfiguredObject.NAME)).thenReturn(hostName);
+        when(vhn.getAttribute(LegacyConfiguredObject.TYPE)).thenReturn("VirtualHostNode");
+        when(vhn.getCategory()).thenReturn("VirtualHostNode");
+
+        Object data = _controller.formatConfiguredObject(object, parameters, true);
+
+        assertThat(data, is(instanceOf(Map.class)));
+        Map<?, ?> formatted = (Map<?, ?>) data;
+
+        assertThat(formatted.get(LegacyConfiguredObject.NAME), is(equalTo(objectName)));
+        assertThat(formatted.get(LegacyConfiguredObject.TYPE), is(equalTo("Broker")));
+
+        Object vhns = formatted.get("virtualhostnodes");
+        assertThat(vhns, is(instanceOf(Collection.class)));
+
+        Collection<?> nodes = (Collection<?>)vhns;
+
+        assertThat(nodes.size(), is(equalTo(1)));
+
+        Object node = nodes.iterator().next();
+        assertThat(node, is(instanceOf(Map.class)));
+        Map<?, ?> formattedNode = (Map<?, ?>) node;
+
+        assertThat(formattedNode.get(LegacyConfiguredObject.NAME), is(equalTo(hostName)));
+        assertThat(formattedNode.get(LegacyConfiguredObject.TYPE), is(equalTo("VirtualHostNode")));
+    }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/ContainerControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/ContainerControllerTest.java
new file mode 100644
index 0000000..896d41b
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/ContainerControllerTest.java
@@ -0,0 +1,83 @@
+/*
+ *
+ * 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.management.plugin.controller.v7_0.category;
+
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class ContainerControllerTest extends UnitTestBase
+{
+    private LegacyCategoryControllerFactory _factory;
+    private LegacyManagementController _nextVersionManagementController;
+    private String MODEL_VERSION = "7.0";
+
+    @Before
+    public void setUp()
+    {
+        _nextVersionManagementController = mock(LegacyManagementController.class);
+        when(_nextVersionManagementController.getVersion()).thenReturn(MODEL_VERSION);
+        _factory = new LegacyCategoryControllerFactory();
+    }
+
+    @Test
+    public void convertNextVersionBrokerConfiguredObject()
+    {
+        final CategoryController controller =
+                _factory.createController("Broker", _nextVersionManagementController);
+
+        assertThat(controller.getCategory(), is(equalTo("Broker")));
+
+        final LegacyConfiguredObject object = mock(LegacyConfiguredObject.class);
+        when(object.getAttribute("modelVersion")).thenReturn("foo");
+        final LegacyConfiguredObject converted = controller.convertFromNextVersion(object);
+
+        Object modelVersion = converted.getAttribute("modelVersion");
+        assertThat(modelVersion, is(equalTo(MODEL_VERSION)));
+    }
+
+    @Test
+    public void convertNextVersionVirtualHostConfiguredObject()
+    {
+        final CategoryController controller =
+                _factory.createController("VirtualHost", _nextVersionManagementController);
+
+        assertThat(controller.getCategory(), is(equalTo("VirtualHost")));
+
+        final LegacyConfiguredObject object = mock(LegacyConfiguredObject.class);
+        when(object.getAttribute("modelVersion")).thenReturn("foo");
+        final LegacyConfiguredObject converted = controller.convertFromNextVersion(object);
+
+        Object modelVersion = converted.getAttribute("modelVersion");
+        assertThat(modelVersion, is(equalTo(MODEL_VERSION)));
+    }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryControllerFactoryTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryControllerFactoryTest.java
new file mode 100644
index 0000000..3964d2e
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v7_0/category/LegacyCategoryControllerFactoryTest.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * 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.management.plugin.controller.v7_0.category;
+
+import static org.apache.qpid.server.management.plugin.controller.v7_0.category.LegacyCategoryControllerFactory.SUPPORTED_CATEGORIES;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class LegacyCategoryControllerFactoryTest extends UnitTestBase
+{
+    private LegacyCategoryControllerFactory _factory;
+    private LegacyManagementController _nextVersionManagementController;
+
+    @Before
+    public void setUp()
+    {
+        _nextVersionManagementController = mock(LegacyManagementController.class);
+        _factory = new LegacyCategoryControllerFactory();
+    }
+
+    @Test
+    public void createController()
+    {
+        SUPPORTED_CATEGORIES.keySet().forEach(category-> {
+            final CategoryController controller =
+                    _factory.createController(category, _nextVersionManagementController);
+            assertThat(controller.getCategory(), is(equalTo(category)));
+        });
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[qpid-broker-j] 01/03: QPID-6948: [Broker-J] Introduce interfaces for handling REST management requests and add pluggable mechanism for supporting previous versions of REST API

Posted by or...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit d0a6a700c6b9b2f9ec1f1885bc4a73c796bd5f2b
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Sat Dec 15 22:42:46 2018 +0000

    QPID-6948: [Broker-J] Introduce interfaces for handling REST management requests and add pluggable mechanism for supporting previous versions of REST API
---
 .../server/management/plugin/HttpManagement.java   |   93 +-
 .../management/plugin/ManagementController.java    |   95 ++
 .../plugin/ManagementControllerFactory.java        |   47 +
 .../management/plugin/ManagementException.java     |  282 ++++++
 .../management/plugin/ManagementRequest.java       |   53 +
 .../management/plugin/ManagementResponse.java      |   34 +
 .../qpid/server/management/plugin/RequestType.java |   29 +
 .../server/management/plugin/ResponseType.java     |   28 +
 .../controller/AbstractManagementController.java   |  204 ++++
 .../plugin/controller/ConverterHelper.java         |  154 +++
 .../latest/LatestManagementController.java         |  863 ++++++++++++++++
 .../latest/LatestManagementControllerFactory.java  |   56 ++
 .../plugin/servlet/rest/RequestInfo.java           |    7 +-
 .../plugin/servlet/rest/RestServlet.java           | 1062 ++++++--------------
 .../servlet/rest/RestUserPreferenceHandler.java    |   19 +-
 .../latest/LatestManagementControllerTest.java     |  809 +++++++++++++++
 .../plugin/servlet/rest/RequestInfoParserTest.java |   23 +-
 17 files changed, 3023 insertions(+), 835 deletions(-)

diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
index b591010..b76ab9d 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
@@ -25,6 +25,7 @@ import java.io.StringWriter;
 import java.io.Writer;
 import java.net.BindException;
 import java.net.InetSocketAddress;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -655,55 +656,59 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
         return factory;
     }
 
-    private void addRestServlet(ServletContextHandler root)
+    private void addRestServlet(final ServletContextHandler root)
     {
-        Set<Class<? extends ConfiguredObject>> categories = new HashSet<>(getModel().getSupportedCategories());
-        final RestServlet restServlet = new RestServlet();
+        final Map<String, ManagementControllerFactory> factories = ManagementControllerFactory.loadFactories();
         final ApiDocsServlet apiDocsServlet = new ApiDocsServlet();
-
-        for (Class<? extends ConfiguredObject> category : categories)
-        {
-            String name = category.getSimpleName().toLowerCase();
-
-            ServletHolder servletHolder = new ServletHolder(name, restServlet);
-            servletHolder.getRegistration().setMultipartConfig(
-                    new MultipartConfigElement("",
-                                               getContextValue(Long.class, MAX_HTTP_FILE_UPLOAD_SIZE_CONTEXT_NAME),
-                                               -1L,
-                                               getContextValue(Integer.class,
-                                                               MAX_HTTP_FILE_UPLOAD_SIZE_CONTEXT_NAME)));
-
-            List<String> paths = Arrays.asList("/api/latest/" + name,
-                                               "/api/v" + BrokerModel.MODEL_VERSION + "/" + name);
-
-            for (String path : paths)
+        final List<String> supportedVersions = new ArrayList<>();
+        String currentVersion = BrokerModel.MODEL_VERSION;
+        ManagementController managementController = null;
+        ManagementControllerFactory factory;
+        do
+        {
+            factory = factories.get(currentVersion);
+            if (factory != null)
             {
-                root.addServlet(servletHolder, path + "/*");
+                managementController = factory.createManagementController(this, managementController);
+                final RestServlet managementServlet = new RestServlet();
+                final Collection<String> categories = managementController.getCategories();
+                for (String category : categories)
+                {
+                    final String name = category.toLowerCase();
+                    final String path = managementController.getCategoryMapping(name);
+                    final ServletHolder servletHolder = new ServletHolder(path, managementServlet);
+                    servletHolder.setInitParameter("qpid.controller.version", managementController.getVersion());
+
+                    servletHolder.getRegistration().setMultipartConfig(
+                            new MultipartConfigElement("",
+                                                       getContextValue(Long.class,
+                                                                       MAX_HTTP_FILE_UPLOAD_SIZE_CONTEXT_NAME),
+                                                       -1L,
+                                                       getContextValue(Integer.class,
+                                                                       MAX_HTTP_FILE_UPLOAD_SIZE_CONTEXT_NAME)));
+
+                    root.addServlet(servletHolder, path + (path.endsWith("/") ? "*" : "/*"));
+
+                    if (BrokerModel.MODEL_VERSION.equals(managementController.getVersion()))
+                    {
+                        root.addServlet(servletHolder, "/api/latest/" + name + "/*");
+                        ServletHolder docServletHolder = new ServletHolder(name + "docs", apiDocsServlet);
+                        root.addServlet(docServletHolder, "/apidocs/latest/" + name + "/");
+                        root.addServlet(docServletHolder, "/apidocs/v" + BrokerModel.MODEL_VERSION + "/" + name + "/");
+                        root.addServlet(docServletHolder, "/apidocs/latest/" + name);
+                        root.addServlet(docServletHolder, "/apidocs/v" + BrokerModel.MODEL_VERSION + "/" + name);
+                    }
+                }
+                supportedVersions.add("v" + currentVersion);
+                currentVersion = factory.getPreviousVersion();
             }
-            ServletHolder docServletHolder = new ServletHolder(name + "docs", apiDocsServlet);
-            root.addServlet(docServletHolder, "/apidocs/latest/" + name + "/");
-            root.addServlet(docServletHolder, "/apidocs/v" + BrokerModel.MODEL_VERSION + "/" + name + "/");
-            root.addServlet(docServletHolder, "/apidocs/latest/" + name);
-            root.addServlet(docServletHolder, "/apidocs/v" + BrokerModel.MODEL_VERSION + "/" + name);
-
-
         }
-
-        final ServletHolder versionsServletHolder = new ServletHolder(new JsonValueServlet(getApiProperties()));
-        root.addServlet(versionsServletHolder,"/api");
-        root.addServlet(versionsServletHolder,"/api/");
-
-    }
-
-    private Map<String, Object> getApiProperties()
-    {
-        return Collections.singletonMap("supportedVersions", getSupportedRestApiVersions());
-    }
-
-    private List<String> getSupportedRestApiVersions()
-    {
-        // TODO - actually support multiple versions and add those versions to the list
-        return Collections.singletonList(getLatestSupportedVersion());
+        while (factory != null);
+        root.getServletContext().setAttribute("qpid.controller.chain", managementController);
+        final Map<String, List<String>> supported = Collections.singletonMap("supportedVersions", supportedVersions);
+        final ServletHolder versionsServletHolder = new ServletHolder(new JsonValueServlet(supported));
+        root.addServlet(versionsServletHolder, "/api");
+        root.addServlet(versionsServletHolder, "/api/");
     }
 
     private String getLatestSupportedVersion()
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementController.java
new file mode 100644
index 0000000..5c923c2
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementController.java
@@ -0,0 +1,95 @@
+/*
+ *
+ * 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.management.plugin;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public interface ManagementController
+{
+    String getVersion();
+
+    Collection<String> getCategories();
+
+    String getCategoryMapping(String category);
+
+    String getCategory(ConfiguredObject<?> managedObject);
+
+    Collection<String> getCategoryHierarchy(ConfiguredObject<?> root, String category);
+
+    ManagementController getNextVersionManagementController();
+
+    ManagementResponse handleGet(ManagementRequest request) throws ManagementException;
+
+    ManagementResponse handlePut(ManagementRequest request) throws ManagementException;
+
+    ManagementResponse handlePost(ManagementRequest request) throws ManagementException;
+
+    ManagementResponse handleDelete(ManagementRequest request) throws ManagementException;
+
+    Object get(ConfiguredObject<?> root,
+               String category,
+               List<String> path,
+               Map<String, List<String>> parameters) throws ManagementException;
+
+    Object createOrUpdate(ConfiguredObject<?> root,
+                          String category,
+                          List<String> path,
+                          Map<String, Object> attributes,
+                          boolean isPost) throws ManagementException;
+
+    int delete(ConfiguredObject<?> root,
+               String category,
+               List<String> path,
+               Map<String, List<String>> parameters) throws ManagementException;
+
+    ManagementResponse invoke(ConfiguredObject<?> root,
+                              String category,
+                              List<String> path,
+                              String operation,
+                              Map<String, Object> parameters,
+                              boolean isPost,
+                              final boolean isSecureOrAllowedOnInsecureChannel) throws ManagementException;
+
+    Object getPreferences(ConfiguredObject<?> root,
+                          String category,
+                          List<String> path,
+                          Map<String, List<String>> parameters) throws ManagementException;
+
+    void setPreferences(ConfiguredObject<?> root,
+                        String category,
+                        List<String> path,
+                        Object preferences,
+                        Map<String, List<String>> parameters,
+                        boolean isPost) throws ManagementException;
+
+    int deletePreferences(ConfiguredObject<?> root,
+                          String category,
+                          List<String> path,
+                          Map<String, List<String>> parameters) throws ManagementException;
+
+    Object formatConfiguredObject(Object content,
+                                  Map<String, List<String>> parameters,
+                                  boolean isSecureOrAllowedOnInsecureChannel);
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementControllerFactory.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementControllerFactory.java
new file mode 100644
index 0000000..8e02513
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementControllerFactory.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * 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.management.plugin;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.apache.qpid.server.plugin.Pluggable;
+import org.apache.qpid.server.plugin.QpidServiceLoader;
+
+public interface ManagementControllerFactory extends Pluggable
+{
+    String getVersion();
+
+    String getPreviousVersion();
+
+    ManagementController createManagementController(HttpManagementConfiguration<?> httpManagement,
+                                                    ManagementController nextVersionManagementController);
+
+    static Map<String, ManagementControllerFactory> loadFactories()
+    {
+        final Iterable<ManagementControllerFactory> factories =
+                new QpidServiceLoader().atLeastOneInstanceOf(ManagementControllerFactory.class);
+
+        return StreamSupport.stream(factories.spliterator(), false)
+                            .collect(Collectors.toMap(ManagementControllerFactory::getVersion, f -> f));
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementException.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementException.java
new file mode 100644
index 0000000..8e7b3c3
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementException.java
@@ -0,0 +1,282 @@
+/*
+ *
+ * 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.management.plugin;
+
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+import javax.security.auth.Subject;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.server.configuration.IllegalConfigurationException;
+import org.apache.qpid.server.management.plugin.controller.ConverterHelper;
+import org.apache.qpid.server.management.plugin.servlet.rest.NotFoundException;
+import org.apache.qpid.server.model.AbstractConfiguredObject;
+import org.apache.qpid.server.model.IllegalStateTransitionException;
+import org.apache.qpid.server.model.IntegrityViolationException;
+import org.apache.qpid.server.model.OperationTimeoutException;
+import org.apache.qpid.server.util.ExternalServiceException;
+import org.apache.qpid.server.util.ExternalServiceTimeoutException;
+
+public class ManagementException extends RuntimeException
+{
+    private static final int SC_UNPROCESSABLE_ENTITY = 422;
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ManagementException.class);
+
+    private final int _statusCode;
+    private final Map<String, String> _headers;
+
+    private ManagementException(final int statusCode,
+                                  final String message,
+                                  final Map<String, String> headers)
+    {
+        super(message);
+        _statusCode = statusCode;
+        _headers = headers;
+    }
+
+    private ManagementException(final int statusCode,
+                                  final String message,
+                                  final Throwable cause,
+                                  final Map<String, String> headers)
+    {
+        super(message, cause);
+        _statusCode = statusCode;
+        _headers = headers;
+    }
+
+    public int getStatusCode()
+    {
+        return _statusCode;
+    }
+
+    public Map<String, String> getHeaders()
+    {
+        return _headers;
+    }
+
+    public static ManagementException createNotFoundManagementException(final Exception e)
+    {
+        return new ManagementException(HttpServletResponse.SC_NOT_FOUND, e.getMessage(), e, null);
+    }
+
+    public static ManagementException createNotFoundManagementException(final String message)
+    {
+        return new ManagementException(HttpServletResponse.SC_NOT_FOUND, message, null);
+    }
+
+    public static ManagementException createGoneManagementException(final String message)
+    {
+        return new ManagementException(HttpServletResponse.SC_GONE, message, null);
+    }
+
+    public static ManagementException createUnprocessableManagementException(final String message)
+    {
+        return new ManagementException(SC_UNPROCESSABLE_ENTITY, message, null);
+    }
+
+    public static ManagementException createUnprocessableManagementException(final Exception e)
+    {
+        return new ManagementException(SC_UNPROCESSABLE_ENTITY, e.getMessage(), e, null);
+    }
+
+    private static ManagementException createConflictManagementException(final Exception e)
+    {
+        return new ManagementException(HttpServletResponse.SC_CONFLICT, e.getMessage(), e, null);
+    }
+
+
+    public static ManagementException createNotAllowedManagementException(final String message,
+                                                                          final Map<String, String> headers)
+    {
+        return new ManagementException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, message, headers);
+    }
+
+    public static ManagementException createForbiddenManagementException(final String message)
+    {
+        return new ManagementException(HttpServletResponse.SC_FORBIDDEN, message, null);
+    }
+
+
+    public static ManagementException createForbiddenManagementException(final Exception e)
+    {
+        return new ManagementException(HttpServletResponse.SC_FORBIDDEN, e.getMessage(), e, null);
+    }
+
+
+    public static ManagementException createInternalServerErrorManagementException(final String message)
+    {
+        return new ManagementException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message, null);
+    }
+
+    public static ManagementException createInternalServerErrorManagementException(final String message,
+                                                                                   final Exception e)
+    {
+        return new ManagementException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message, e, null);
+    }
+
+
+    private static ManagementException createBadGatewayManagementException(final String message,
+                                                                           final RuntimeException e)
+    {
+        return new ManagementException(HttpServletResponse.SC_BAD_GATEWAY, message, e, null);
+    }
+
+    private static ManagementException createGatewayTimeoutManagementException(final RuntimeException e)
+    {
+        return new ManagementException(HttpServletResponse.SC_GATEWAY_TIMEOUT, e.getMessage(), e, null);
+    }
+
+
+    public static ManagementException createBadRequestManagementException(final String message)
+    {
+        return new ManagementException(HttpServletResponse.SC_BAD_REQUEST, message, null);
+    }
+
+    public static ManagementException createBadRequestManagementException(final String message, final Throwable e)
+    {
+        return new ManagementException(HttpServletResponse.SC_BAD_REQUEST, message, e, null);
+    }
+
+
+    public static ManagementException toManagementException(final RuntimeException e,
+                                                            final String categoryMapping,
+                                                            final List<String> path)
+    {
+        if (e instanceof SecurityException)
+        {
+            LOGGER.debug("{}, sending {}", e.getClass().getName(), HttpServletResponse.SC_FORBIDDEN, e);
+            return createForbiddenManagementException(e);
+        }
+        else if (e instanceof AbstractConfiguredObject.DuplicateIdException
+                 || e instanceof AbstractConfiguredObject.DuplicateNameException
+                 || e instanceof IntegrityViolationException
+                 || e instanceof IllegalStateTransitionException)
+        {
+            return createConflictManagementException(e);
+        }
+        else if (e instanceof NotFoundException)
+        {
+            if (LOGGER.isTraceEnabled())
+            {
+                LOGGER.trace(e.getClass().getSimpleName() + " processing request", e);
+            }
+            return createNotFoundManagementException(e);
+        }
+        else if (e instanceof IllegalConfigurationException || e instanceof IllegalArgumentException)
+        {
+            LOGGER.warn("{} processing request {} from user '{}': {}",
+                        e.getClass().getSimpleName(),
+                        getRequestURI(path, categoryMapping),
+                        getRequestPrincipals(),
+                        e.getMessage());
+            Throwable t = e;
+            int maxDepth = 10;
+            while ((t = t.getCause()) != null && maxDepth-- != 0)
+            {
+                LOGGER.warn("... caused by " + t.getClass().getSimpleName() + "  : " + t.getMessage());
+            }
+            if (LOGGER.isDebugEnabled())
+            {
+                LOGGER.debug(e.getClass().getSimpleName() + " processing request", e);
+            }
+            return createUnprocessableManagementException(e);
+        }
+        else if (e instanceof OperationTimeoutException)
+        {
+            if (LOGGER.isDebugEnabled())
+            {
+                LOGGER.debug("Timeout during processing of request {} from user '{}'",
+                             getRequestURI(path, categoryMapping),
+                             getRequestPrincipals(),
+                             e);
+            }
+            else
+            {
+                LOGGER.info("Timeout during processing of request {} from user '{}'",
+                            getRequestURI(path, categoryMapping), getRequestPrincipals());
+            }
+            return createBadGatewayManagementException("Timeout occurred", e);
+        }
+
+        else if (e instanceof ExternalServiceTimeoutException)
+        {
+            LOGGER.warn("External request timeout ", e);
+            return createGatewayTimeoutManagementException(e);
+        }
+        else if (e instanceof ExternalServiceException)
+        {
+            LOGGER.warn("External request failed ", e);
+            return createBadGatewayManagementException(e.getMessage(), e);
+        }
+        else if (e instanceof ManagementException)
+        {
+            return (ManagementException)e;
+        }
+        else
+        {
+            LOGGER.warn("Unexpected Exception", e);
+            return createInternalServerErrorManagementException("Unexpected Exception", e);
+        }
+    }
+
+    public static ManagementException handleError(final Error e)
+    {
+        if (e instanceof NoClassDefFoundError)
+        {
+            LOGGER.warn("Unexpected exception processing request ", e);
+            return createBadRequestManagementException("Not found: " + e.getMessage(), e);
+        }
+        else
+        {
+            throw e;
+        }
+    }
+
+    public static String getRequestURI(final List<String> path, final String categoryMapping)
+    {
+        return categoryMapping + (categoryMapping.endsWith("/") ? "" : "/")
+               + path.stream().map(ConverterHelper::encode).collect(Collectors.joining("/"));
+    }
+
+    private static String getRequestPrincipals()
+    {
+        final Subject subject = Subject.getSubject(AccessController.getContext());
+        if (subject == null)
+        {
+            return null;
+        }
+        final Set<Principal> principalSet = subject.getPrincipals();
+        return String.join("/",
+                           principalSet.stream()
+                                       .map(Principal::getName)
+                                       .collect(Collectors.toCollection(TreeSet::new)));
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementRequest.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementRequest.java
new file mode 100644
index 0000000..8b5c1ca
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementRequest.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public interface ManagementRequest
+{
+    String getMethod();
+
+    Map<String, List<String>> getParameters();
+
+    String getParameter(String name);
+
+    Map<String, Object> getParametersAsFlatMap();
+
+    Map<String, String> getHeaders();
+
+    List<String> getPath();
+
+    String getCategory();
+
+    ConfiguredObject<?> getRoot();
+
+    boolean isSecure();
+
+    boolean isConfidentialOperationAllowedOnInsecureChannel();
+
+    <T> T getBody(Class<T> type);
+
+    String getRequestURL();
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementResponse.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementResponse.java
new file mode 100644
index 0000000..a000619
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementResponse.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * 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.management.plugin;
+
+import java.util.Map;
+
+public interface ManagementResponse
+{
+    ResponseType getType();
+
+    Object getBody();
+
+    Map<String, String> getHeaders();
+
+    int getResponseCode();
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/RequestType.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/RequestType.java
new file mode 100644
index 0000000..29ca542
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/RequestType.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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.management.plugin;
+
+public enum RequestType
+{
+    OPERATION,
+    USER_PREFERENCES,
+    VISIBLE_PREFERENCES,
+    MODEL_OBJECT
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ResponseType.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ResponseType.java
new file mode 100644
index 0000000..8396075
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ResponseType.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * 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.management.plugin;
+
+public enum ResponseType
+{
+    MODEL_OBJECT,
+    DATA,
+    EMPTY
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/AbstractManagementController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/AbstractManagementController.java
new file mode 100644
index 0000000..3888869
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/AbstractManagementController.java
@@ -0,0 +1,204 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import static org.apache.qpid.server.management.plugin.controller.ConverterHelper.encode;
+import static org.apache.qpid.server.management.plugin.ManagementException.createBadRequestManagementException;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.RequestType;
+import org.apache.qpid.server.management.plugin.ManagementRequest;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+
+public abstract class AbstractManagementController implements ManagementController
+{
+    protected static final String USER_PREFERENCES = "userpreferences";
+    protected static final String VISIBLE_USER_PREFERENCES = "visiblepreferences";
+
+    public ManagementResponse handleGet(final ManagementRequest request) throws ManagementException
+    {
+        final RequestType type = getRequestType(request);
+        switch (type)
+        {
+            case OPERATION:
+            {
+                final Collection<String> hierarchy = getCategoryHierarchy(request.getRoot(), request.getCategory());
+                final List<String> operationPath = request.getPath().subList(0, hierarchy.size());
+                final String operationName = request.getPath().get(hierarchy.size());
+                return invoke(request.getRoot(),
+                                  request.getCategory(),
+                                  operationPath,
+                                  operationName,
+                                  request.getParametersAsFlatMap(),
+                                  false,
+                                  request.isSecure() || request.isConfidentialOperationAllowedOnInsecureChannel());
+            }
+            case MODEL_OBJECT:
+            {
+                final Object response = get(request.getRoot(),
+                                            request.getCategory(),
+                                            request.getPath(),
+                                            request.getParameters());
+
+                return new ControllerManagementResponse(ResponseType.MODEL_OBJECT, response);
+            }
+            case VISIBLE_PREFERENCES:
+            case USER_PREFERENCES:
+            {
+                final Object response = getPreferences(request.getRoot(),
+                                          request.getCategory(),
+                                          request.getPath(),
+                                          request.getParameters());
+                return new ControllerManagementResponse(ResponseType.DATA, response);
+            }
+            default:
+            {
+                throw createBadRequestManagementException(String.format("Unexpected request type '%s' for path '%s'",
+                                                                        type,
+                                                                        getCategoryMapping(request.getCategory())));
+            }
+        }
+
+
+    }
+
+    public ManagementResponse handlePut(final ManagementRequest request) throws ManagementException
+    {
+        return handlePostOrPut(request);
+    }
+
+    public ManagementResponse handlePost(final ManagementRequest request) throws ManagementException
+    {
+        return handlePostOrPut(request);
+    }
+
+    public ManagementResponse handleDelete(final ManagementRequest request) throws ManagementException
+    {
+        final RequestType type = getRequestType(request);
+        switch (type)
+        {
+            case MODEL_OBJECT:
+            {
+                delete(request.getRoot(),
+                       request.getCategory(),
+                       request.getPath(),
+                       request.getParameters());
+                break;
+            }
+            case VISIBLE_PREFERENCES:
+            case USER_PREFERENCES:
+            {
+                deletePreferences(request.getRoot(),
+                                  request.getCategory(),
+                                  request.getPath(),
+                                  request.getParameters());
+                break;
+            }
+            default:
+            {
+                throw createBadRequestManagementException(String.format("Unexpected request type '%s' for path '%s'",
+                                                                        type,
+                                                                        getCategoryMapping(request.getCategory())));
+            }
+        }
+        return new ControllerManagementResponse(ResponseType.EMPTY, null);
+    }
+
+    private ManagementResponse handlePostOrPut(final ManagementRequest request) throws ManagementException
+    {
+        final RequestType type = getRequestType(request);
+        final Collection<String> hierarchy = getCategoryHierarchy(request.getRoot(), request.getCategory());
+        switch (type)
+        {
+            case OPERATION:
+            {
+                final List<String> operationPath = request.getPath().subList(0, hierarchy.size());
+                final String operationName = request.getPath().get(hierarchy.size());
+                @SuppressWarnings("unchecked")
+                final Map<String, Object> arguments = request.getBody(LinkedHashMap.class);
+                return invoke(request.getRoot(),
+                                  request.getCategory(),
+                                  operationPath,
+                                  operationName,
+                                  arguments,
+                                  true,
+                                  request.isSecure()
+                                  || request.isConfidentialOperationAllowedOnInsecureChannel());
+            }
+            case MODEL_OBJECT:
+            {
+                @SuppressWarnings("unchecked")
+                final Map<String, Object> attributes = request.getBody(LinkedHashMap.class);
+                final Object response = createOrUpdate(request.getRoot(),
+                                          request.getCategory(),
+                                          request.getPath(),
+                                          attributes,
+                                          "POST".equalsIgnoreCase(request.getMethod()));
+                int responseCode = HttpServletResponse.SC_OK;
+                ResponseType responseType = ResponseType.EMPTY;
+                Map<String, String> headers = Collections.emptyMap();
+                if (response != null)
+                {
+                    responseCode = HttpServletResponse.SC_CREATED;
+                    final StringBuilder requestURL = new StringBuilder(request.getRequestURL());
+                    if (hierarchy.size() != request.getPath().size())
+                    {
+                        Object name = attributes.get("name");
+                        requestURL.append("/").append(encode(String.valueOf(name)));
+                    }
+                    headers = Collections.singletonMap("Location", requestURL.toString());
+                    responseType = ResponseType.MODEL_OBJECT;
+                }
+                return new ControllerManagementResponse(responseType, response, responseCode, headers);
+            }
+            case VISIBLE_PREFERENCES:
+            case USER_PREFERENCES:
+            {
+                setPreferences(request.getRoot(),
+                               request.getCategory(),
+                               request.getPath(),
+                               request.getBody(Object.class),
+                               request.getParameters(),
+                               "POST".equalsIgnoreCase(request.getMethod()));
+                return new ControllerManagementResponse(ResponseType.EMPTY, null);
+            }
+            default:
+            {
+                throw createBadRequestManagementException(String.format("Unexpected request type '%s' for path '%s'",
+                                                                        type,
+                                                                        getCategoryMapping(request.getCategory())));
+            }
+        }
+
+    }
+
+    protected abstract RequestType getRequestType(ManagementRequest managementRequest) throws ManagementException;
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/ConverterHelper.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/ConverterHelper.java
new file mode 100644
index 0000000..b4fc180
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/ConverterHelper.java
@@ -0,0 +1,154 @@
+/*
+ *
+ * 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.management.plugin.controller;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.qpid.server.management.plugin.HttpManagementUtil;
+import org.apache.qpid.server.management.plugin.ManagementException;
+
+public class ConverterHelper
+{
+
+    private static final Pattern CONTEXT_VARIABLE_PATTERN = Pattern.compile("\\$\\{[\\w+.\\-:]+}");
+
+    private ConverterHelper()
+    {
+        super();
+    }
+
+    public static long toLong(final Object attributeValue)
+    {
+        long value;
+        if (attributeValue instanceof Number)
+        {
+            value = ((Number) attributeValue).longValue();
+        }
+        else if (attributeValue instanceof String)
+        {
+            try
+            {
+                value = Long.parseLong((String) attributeValue);
+            }
+            catch (Exception e)
+            {
+                value = 0;
+            }
+        }
+        else
+        {
+            value = 0;
+        }
+        return value;
+    }
+
+    public static boolean toBoolean(final Object attributeValue)
+    {
+        boolean value;
+        if (attributeValue instanceof Boolean)
+        {
+            value = (Boolean) attributeValue;
+        }
+        else if (attributeValue instanceof String)
+        {
+            return Boolean.parseBoolean((String)attributeValue);
+
+        }
+        else
+        {
+            value = false;
+        }
+        return value;
+    }
+
+    public static int toInt(final Object value)
+    {
+        int result;
+        if (value instanceof Number)
+        {
+            result = ((Number) value).intValue();
+        }
+        else if (value instanceof String)
+        {
+            try
+            {
+                result = Integer.parseInt(String.valueOf(value));
+            }
+            catch (RuntimeException e)
+            {
+                result = 0;
+            }
+        }
+        else
+        {
+            result = 0;
+        }
+        return result;
+    }
+
+    public static  boolean isContextVariable(final Object value)
+    {
+        return value != null && CONTEXT_VARIABLE_PATTERN.matcher(String.valueOf(value)).matches();
+    }
+
+    public static String encode (String value)
+    {
+        try
+        {
+            return URLEncoder.encode(value, StandardCharsets.UTF_8.displayName());
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw ManagementException.createInternalServerErrorManagementException("UTF8 encoding is unsupported", e);
+        }
+    }
+
+    public static String getParameter(String name, Map<String, List<String>> parameters)
+    {
+        List<String> values = parameters.get(name);
+        return values == null || values.isEmpty() ? null : values.get(0);
+    }
+
+    public static int getIntParameterFromRequest(final Map<String, List<String>> parameters,
+                                                 final String parameterName,
+                                                 final int defaultValue)
+    {
+        int intValue = defaultValue;
+        final String stringValue = getParameter(parameterName, parameters);
+        if (stringValue != null)
+        {
+            try
+            {
+                intValue = Integer.parseInt(stringValue);
+            }
+            catch (NumberFormatException e)
+            {
+                // noop
+            }
+        }
+        return intValue;
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementController.java
new file mode 100644
index 0000000..18b457a
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementController.java
@@ -0,0 +1,863 @@
+/*
+ *
+ * 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.management.plugin.controller.latest;
+
+import static org.apache.qpid.server.management.plugin.HttpManagementConfiguration.DEFAULT_PREFERENCE_OPERATION_TIMEOUT;
+import static org.apache.qpid.server.management.plugin.HttpManagementConfiguration.PREFERENCE_OPERTAION_TIMEOUT_CONTEXT_NAME;
+import static org.apache.qpid.server.management.plugin.ManagementException.createBadRequestManagementException;
+import static org.apache.qpid.server.management.plugin.ManagementException.createForbiddenManagementException;
+import static org.apache.qpid.server.management.plugin.ManagementException.createNotAllowedManagementException;
+import static org.apache.qpid.server.management.plugin.ManagementException.createNotFoundManagementException;
+import static org.apache.qpid.server.management.plugin.controller.ConverterHelper.getParameter;
+import static org.apache.qpid.server.management.plugin.servlet.rest.AbstractServlet.CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM;
+import static org.apache.qpid.server.model.ConfiguredObjectTypeRegistry.returnsCollectionOfConfiguredObjects;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementRequest;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.RequestType;
+import org.apache.qpid.server.management.plugin.ResponseType;
+import org.apache.qpid.server.management.plugin.controller.AbstractManagementController;
+import org.apache.qpid.server.management.plugin.controller.ControllerManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.ConverterHelper;
+import org.apache.qpid.server.management.plugin.servlet.rest.ConfiguredObjectToMapConverter;
+import org.apache.qpid.server.management.plugin.servlet.rest.NotFoundException;
+import org.apache.qpid.server.management.plugin.servlet.rest.RequestInfo;
+import org.apache.qpid.server.management.plugin.servlet.rest.RestUserPreferenceHandler;
+import org.apache.qpid.server.model.AbstractConfigurationChangeListener;
+import org.apache.qpid.server.model.BrokerModel;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectFinder;
+import org.apache.qpid.server.model.ConfiguredObjectOperation;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.model.State;
+import org.apache.qpid.server.model.preferences.UserPreferences;
+
+public class LatestManagementController extends AbstractManagementController
+{
+    private static final Logger LOGGER = LoggerFactory.getLogger(LatestManagementController.class);
+
+    private static final String DEPTH_PARAM = "depth";
+    private static final String OVERSIZE_PARAM = "oversize";
+    private static final String ACTUALS_PARAM = "actuals";
+    private static final String SORT_PARAM = "sort";
+    private static final String EXTRACT_INITIAL_CONFIG_PARAM = "extractInitialConfig";
+    private static final String EXCLUDE_INHERITED_CONTEXT_PARAM = "excludeInheritedContext";
+    private static final String SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST = "singletonModelObjectResponseAsList";
+    private static final Set<String> RESERVED_PARAMS =
+            new HashSet<>(Arrays.asList(DEPTH_PARAM,
+                                        SORT_PARAM,
+                                        OVERSIZE_PARAM,
+                                        ACTUALS_PARAM,
+                                        EXTRACT_INITIAL_CONFIG_PARAM,
+                                        CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM,
+                                        EXCLUDE_INHERITED_CONTEXT_PARAM,
+                                        SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST));
+
+    private static final int DEFAULT_DEPTH = 0;
+    private static final int DEFAULT_OVERSIZE = 120;
+
+
+    private final ConcurrentMap<ConfiguredObject<?>, ConfiguredObjectFinder> _configuredObjectFinders =
+            new ConcurrentHashMap<>();
+    private final Set<String> _supportedCategories;
+
+    private final ConfiguredObjectToMapConverter _objectConverter = new ConfiguredObjectToMapConverter();
+    private final RestUserPreferenceHandler _userPreferenceHandler;
+
+
+    LatestManagementController(final HttpManagementConfiguration<?> httpManagement)
+    {
+        final Long preferenceOperationTimeout =
+                httpManagement.getContextValue(Long.class, PREFERENCE_OPERTAION_TIMEOUT_CONTEXT_NAME);
+        _userPreferenceHandler = new RestUserPreferenceHandler(preferenceOperationTimeout == null
+                                                                       ? DEFAULT_PREFERENCE_OPERATION_TIMEOUT
+                                                                       : preferenceOperationTimeout);
+        _supportedCategories = Collections.unmodifiableSet(BrokerModel.getInstance()
+                                                                      .getSupportedCategories()
+                                                                      .stream()
+                                                                      .map(Class::getSimpleName)
+                                                                      .collect(Collectors.toSet()));
+    }
+
+    @Override
+    public String getVersion()
+    {
+        return BrokerModel.MODEL_VERSION;
+    }
+
+    @Override
+    public Set<String> getCategories()
+    {
+        return _supportedCategories;
+    }
+
+    @Override
+    public String getCategoryMapping(final String category)
+    {
+        return String.format("/api/v%s/%s/", getVersion(), category.toLowerCase());
+    }
+
+    @Override
+    public String getCategory(final ConfiguredObject<?> managedObject)
+    {
+        return managedObject.getCategoryClass().getSimpleName();
+    }
+
+    @Override
+    public List<String> getCategoryHierarchy(final ConfiguredObject<?> root, final String category)
+    {
+        ConfiguredObjectFinder finder = getConfiguredObjectFinder(root);
+        Class<? extends ConfiguredObject>[] hierarchy = finder.getHierarchy(category.toLowerCase());
+        if (hierarchy == null)
+        {
+            return Collections.emptyList();
+        }
+        return Arrays.stream(hierarchy).map(Class::getSimpleName).collect(Collectors.toList());
+    }
+
+    @Override
+    public ManagementController getNextVersionManagementController()
+    {
+        return null;
+    }
+
+    @Override
+    protected RequestType getRequestType(final ManagementRequest request) throws ManagementException
+    {
+        final ConfiguredObject<?> root = request.getRoot();
+        if (root == null)
+        {
+            final String message =
+                    String.format("No HTTP Management alias mapping found for '%s'", request.getRequestURL());
+            LOGGER.info(message);
+            throw createNotFoundManagementException(message);
+        }
+        final ConfiguredObjectFinder finder = getConfiguredObjectFinder(root);
+        final String category = request.getCategory();
+        final Class<? extends ConfiguredObject> configuredClass = getRequestCategoryClass(category, root.getModel());
+        final Class<? extends ConfiguredObject>[] hierarchy = finder.getHierarchy(configuredClass);
+        return getManagementRequestType(request.getMethod(), category, request.getPath(), hierarchy);
+    }
+
+    @Override
+    public Object get(final ConfiguredObject<?> root,
+                      final String category,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        try
+        {
+            final Predicate<ConfiguredObject<?>> filterPredicate = buildFilterPredicates(parameters);
+            final boolean singleObjectRequest = isFullPath(root, path, category) && !hasFilter(parameters);
+            final Collection<ConfiguredObject<?>> allObjects = getTargetObjects(root, category, path, filterPredicate);
+            if (singleObjectRequest)
+            {
+                if (allObjects.isEmpty())
+                {
+                    throw createNotFoundManagementException("Not Found");
+                }
+                else if (allObjects.size() != 1)
+                {
+                    throw createBadRequestManagementException(String.format(
+                            "Unexpected number of objects found [%d] for singleton request URI '%s'",
+                            allObjects.size(), ManagementException.getRequestURI(path, getCategoryMapping(category))));
+                }
+                else
+                {
+                    return allObjects.iterator().next();
+                }
+            }
+            else
+            {
+                return allObjects;
+            }
+        }
+        catch (RuntimeException e)
+        {
+            throw ManagementException.toManagementException(e, getCategoryMapping(category), path);
+        }
+        catch (Error e)
+        {
+            throw ManagementException.handleError(e);
+        }
+     }
+
+    @Override
+    public ConfiguredObject<?> createOrUpdate(final ConfiguredObject<?> root,
+                                              final String category,
+                                              final List<String> path,
+                                              final Map<String, Object> providedObject,
+                                              final boolean isPost) throws ManagementException
+    {
+        try
+        {
+            final List<String> hierarchy = getCategoryHierarchy(root, category);
+            if (path.isEmpty() && hierarchy.size() == 0)
+            {
+                root.setAttributes(providedObject);
+                return null;
+            }
+            final Class<? extends ConfiguredObject> categoryClass = getRequestCategoryClass(category, root.getModel());
+            ConfiguredObject theParent = root;
+            if (hierarchy.size() > 1)
+            {
+                final ConfiguredObjectFinder finder = getConfiguredObjectFinder(root);
+                theParent = finder.findObjectParentsFromPath(path, finder.getHierarchy(categoryClass), categoryClass);
+            }
+
+            final boolean isFullObjectURL = path.size() == hierarchy.size();
+            if (isFullObjectURL)
+            {
+                final String name = path.get(path.size() - 1);
+                final ConfiguredObject<?> configuredObject = theParent.getChildByName(categoryClass, name);
+                if (configuredObject != null)
+                {
+                    configuredObject.setAttributes(providedObject);
+                    return null;
+                }
+                else if (isPost)
+                {
+                    throw createNotFoundManagementException(String.format("%s '%s' not found",
+                                                                          categoryClass.getSimpleName(),
+                                                                          name));
+                }
+                else
+                {
+                    providedObject.put(ConfiguredObject.NAME, name);
+                }
+            }
+
+            return theParent.createChild(categoryClass, providedObject);
+        }
+        catch (RuntimeException e)
+        {
+            throw ManagementException.toManagementException(e, getCategoryMapping(category), path);
+        }
+        catch (Error e)
+        {
+            throw ManagementException.handleError(e);
+        }
+    }
+
+    @Override
+    public int delete(final ConfiguredObject<?> root,
+                      final String category,
+                      final List<String> names,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        int counter = 0;
+        try
+        {
+            final Predicate<ConfiguredObject<?>> filterPredicate = buildFilterPredicates(parameters);
+            final Collection<ConfiguredObject<?>> allObjects = getTargetObjects(root, category, names, filterPredicate);
+            if (allObjects.isEmpty())
+            {
+                throw createNotFoundManagementException("Not Found");
+            }
+
+            for (ConfiguredObject o : allObjects)
+            {
+                o.delete();
+                counter++;
+            }
+        }
+        catch (RuntimeException e)
+        {
+            throw ManagementException.toManagementException(e, getCategoryMapping(category), names);
+        }
+        catch (Error e)
+        {
+            throw ManagementException.handleError(e);
+        }
+        return counter;
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public ManagementResponse invoke(final ConfiguredObject<?> root,
+                                     final String category,
+                                     final List<String> names,
+                                     final String operationName,
+                                     final Map<String, Object> operationArguments,
+                                     final boolean isPost,
+                                     final boolean isSecureOrAllowedOnInsecureChannel) throws ManagementException
+    {
+        ResponseType responseType = ResponseType.DATA;
+        Object returnValue;
+        try
+        {
+            final ConfiguredObject<?> target = getTarget(root, category, names);
+            final Map<String, ConfiguredObjectOperation<?>> availableOperations =
+                    root.getModel().getTypeRegistry().getOperations(target.getClass());
+            final ConfiguredObjectOperation operation = availableOperations.get(operationName);
+            if (operation == null)
+            {
+                throw createNotFoundManagementException(String.format("No such operation '%s' in '%s'",
+                                                                      operationName,
+                                                                      category));
+            }
+
+            if (operation.isSecure(target, operationArguments) && !isSecureOrAllowedOnInsecureChannel)
+            {
+                throw createForbiddenManagementException(String.format(
+                        "Operation '%s' can only be performed over a secure (HTTPS) connection",
+                        operationName));
+            }
+
+            if (!isPost && !operation.isNonModifying())
+            {
+                throw createNotAllowedManagementException(String.format(
+                        "Operation '%s' modifies the object so you must use POST.",
+                        operationName), Collections.singletonMap("Allow", "POST"));
+            }
+
+            returnValue = operation.perform(target, operationArguments);
+
+            if (ConfiguredObject.class.isAssignableFrom(operation.getReturnType())
+                || returnsCollectionOfConfiguredObjects(operation))
+            {
+                responseType = ResponseType.MODEL_OBJECT;
+            }
+
+        }
+        catch (RuntimeException e)
+        {
+            throw ManagementException.toManagementException(e, getCategoryMapping(category), names);
+        }
+        catch (Error e)
+        {
+            throw ManagementException.handleError(e);
+        }
+        return new ControllerManagementResponse(responseType,returnValue);
+    }
+
+    @Override
+    public Object getPreferences(final ConfiguredObject<?> root,
+                                 final String category,
+                                 final List<String> path,
+                                 final Map<String, List<String>> parameters) throws ManagementException
+    {
+        Object responseObject;
+        try
+        {
+            final List<String> hierarchy = getCategoryHierarchy(root, category);
+            final Collection<ConfiguredObject<?>> allObjects = getTargetObjects(root, category, path, null);
+            if (allObjects.isEmpty() && isFullPath(root, path, category))
+            {
+                throw createNotFoundManagementException("Not Found");
+            }
+
+            final RequestInfo requestInfo = RequestInfo.createPreferencesRequestInfo(path.subList(0, hierarchy.size()),
+                                                                                     path.subList(hierarchy.size() + 1,
+                                                                                                  path.size()),
+                                                                                     parameters);
+
+            if (path.contains("*"))
+            {
+                List<Object> preferencesList = new ArrayList<>(allObjects.size());
+                responseObject = preferencesList;
+                for (ConfiguredObject<?> target : allObjects)
+                {
+                    try
+                    {
+                        final UserPreferences userPreferences = target.getUserPreferences();
+                        final Object preferences = _userPreferenceHandler.handleGET(userPreferences, requestInfo);
+                        if (preferences == null
+                            || (preferences instanceof Collection && ((Collection) preferences).isEmpty())
+                            || (preferences instanceof Map && ((Map) preferences).isEmpty()))
+                        {
+                            continue;
+                        }
+                        preferencesList.add(preferences);
+                    }
+                    catch (NotFoundException e)
+                    {
+                        // The case where the preference's type and name is provided, but this particular object does not
+                        // have a matching preference.
+                    }
+                }
+            }
+            else
+            {
+                final ConfiguredObject<?> target = allObjects.iterator().next();
+                final UserPreferences userPreferences = target.getUserPreferences();
+                responseObject = _userPreferenceHandler.handleGET(userPreferences, requestInfo);
+            }
+        }
+        catch (RuntimeException e)
+        {
+            throw ManagementException.toManagementException(e, getCategoryMapping(category), path);
+        }
+        catch (Error e)
+        {
+            throw ManagementException.handleError(e);
+        }
+        return responseObject;
+    }
+
+    @Override
+    public void setPreferences(final ConfiguredObject<?> root,
+                               final String category,
+                               final List<String> path,
+                               final Object providedObject,
+                               final Map<String, List<String>> parameters,
+                               final boolean isPost) throws ManagementException
+
+    {
+        try
+        {
+            final List<String> hierarchy = getCategoryHierarchy(root, category);
+            final RequestInfo requestInfo = RequestInfo.createPreferencesRequestInfo(path.subList(0, hierarchy.size()),
+                                                                                     path.subList(hierarchy.size() + 1,
+                                                                                                  path.size()),
+                                                                                     parameters);
+            final ConfiguredObject<?> target = getTarget(root, category, requestInfo.getModelParts());
+            if (isPost)
+            {
+                _userPreferenceHandler.handlePOST(target, requestInfo, providedObject);
+            }
+            else
+            {
+                _userPreferenceHandler.handlePUT(target, requestInfo, providedObject);
+            }
+        }
+        catch (RuntimeException e)
+        {
+            throw ManagementException.toManagementException(e, getCategoryMapping(category), path);
+        }
+        catch (Error e)
+        {
+            throw ManagementException.handleError(e);
+        }
+    }
+
+    @Override
+    public int deletePreferences(final ConfiguredObject<?> root,
+                                 final String category,
+                                 final List<String> names,
+                                 final Map<String, List<String>> parameters) throws ManagementException
+    {
+        int counter = 0;
+        try
+        {
+            final List<String> hierarchy = getCategoryHierarchy(root, category);
+            final RequestInfo requestInfo = RequestInfo.createPreferencesRequestInfo(names.subList(0, hierarchy.size()),
+                                                                                     names.subList(hierarchy.size() + 1,
+                                                                                                   names.size()),
+                                                                                     parameters);
+            final Collection<ConfiguredObject<?>> objects = getTargetObjects(root,
+                                                                             category,
+                                                                             requestInfo.getModelParts(),
+                                                                             buildFilterPredicates(parameters));
+            if (objects == null)
+            {
+                throw createNotFoundManagementException("Not Found");
+            }
+
+            //TODO: define format how to report the results for bulk delete, i.e. how to report individual errors and success statuses
+            if (objects.size() > 1)
+            {
+                throw createBadRequestManagementException("Deletion of user preferences using wildcards is unsupported");
+            }
+
+            for (ConfiguredObject o : objects)
+            {
+                _userPreferenceHandler.handleDELETE(o.getUserPreferences(), requestInfo);
+                counter++;
+            }
+        }
+        catch (RuntimeException e)
+        {
+            throw ManagementException.toManagementException(e, getCategoryMapping(category), names);
+        }
+        catch (Error e)
+        {
+            throw ManagementException.handleError(e);
+        }
+        return counter;
+    }
+
+    @Override
+    public Object formatConfiguredObject(final Object content,
+                                         final Map<String, List<String>> parameters,
+                                         final boolean isSecureOrAllowedOnInsecureChannel)
+    {
+
+        final int depth = ConverterHelper.getIntParameterFromRequest(parameters, DEPTH_PARAM, DEFAULT_DEPTH);
+        final int oversizeThreshold = ConverterHelper.getIntParameterFromRequest(parameters, OVERSIZE_PARAM, DEFAULT_OVERSIZE);
+        final boolean actuals = Boolean.parseBoolean(getParameter(ACTUALS_PARAM, parameters));
+        final String excludeInheritedContextParameter = getParameter(EXCLUDE_INHERITED_CONTEXT_PARAM, parameters);
+        final boolean excludeInheritedContext = excludeInheritedContextParameter == null
+                                                || Boolean.parseBoolean(excludeInheritedContextParameter);
+        final boolean responseAsList =
+                Boolean.parseBoolean(getParameter(SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST, parameters));
+
+        if (content instanceof ConfiguredObject)
+        {
+            Object object = convertObject(
+                    (ConfiguredObject) content,
+                    depth,
+                    actuals,
+                    oversizeThreshold,
+                    isSecureOrAllowedOnInsecureChannel,
+                    excludeInheritedContext);
+            return responseAsList ? Collections.singletonList(object) : object;
+        }
+        else if (content instanceof Collection)
+        {
+            Collection<Map<String,Object>> results = ((Collection<?>) content).stream()
+                                                                              .filter(o -> o instanceof ConfiguredObject)
+                                                                              .map(ConfiguredObject.class::cast)
+                                                                              .map(o -> convertObject(
+                                                                                      o,
+                                                                                      depth,
+                                                                                      actuals,
+                                                                                      oversizeThreshold,
+                                                                                      isSecureOrAllowedOnInsecureChannel,
+                                                                                      excludeInheritedContext)).collect(Collectors.toSet());
+            if (!results.isEmpty())
+            {
+                return results;
+            }
+        }
+        return content;
+    }
+
+    private Map<String,Object> convertObject(final ConfiguredObject<?> configuredObject, final int depth,
+                                 final boolean actuals,
+                                 final int oversizeThreshold,
+                                 final boolean isSecureOrConfidentialOperationAllowedOnInsecureChannel,
+                                 final boolean excludeInheritedContext)
+    {
+        return _objectConverter.convertObjectToMap(configuredObject, configuredObject.getCategoryClass(),
+                                                   new ConfiguredObjectToMapConverter.ConverterOptions(
+                                                           depth,
+                                                           actuals,
+                                                           oversizeThreshold,
+                                                           isSecureOrConfidentialOperationAllowedOnInsecureChannel,
+                                                           excludeInheritedContext));
+    }
+
+    private boolean isFullPath(final ConfiguredObject root, final List<String> parts, final String category)
+    {
+        List<String> hierarchy = getCategoryHierarchy(root, category);
+        return parts.size() == hierarchy.size() && !parts.contains("*");
+    }
+
+    private ConfiguredObjectFinder getConfiguredObjectFinder(final ConfiguredObject<?> root)
+    {
+        ConfiguredObjectFinder finder = _configuredObjectFinders.get(root);
+        if (finder == null)
+        {
+            finder = new ConfiguredObjectFinder(root);
+            final ConfiguredObjectFinder existingValue = _configuredObjectFinders.putIfAbsent(root, finder);
+            if (existingValue != null)
+            {
+                finder = existingValue;
+            }
+            else
+            {
+                final AbstractConfigurationChangeListener deletionListener =
+                        new AbstractConfigurationChangeListener()
+                        {
+                            @Override
+                            public void stateChanged(final ConfiguredObject<?> object,
+                                                     final State oldState,
+                                                     final State newState)
+                            {
+                                if (newState == State.DELETED)
+                                {
+                                    _configuredObjectFinders.remove(root);
+                                }
+                            }
+                        };
+                root.addChangeListener(deletionListener);
+                if (root.getState() == State.DELETED)
+                {
+                    _configuredObjectFinders.remove(root);
+                    root.removeChangeListener(deletionListener);
+                }
+            }
+        }
+        return finder;
+    }
+
+    private Collection<ConfiguredObject<?>> getTargetObjects(final ConfiguredObject<?> root,
+                                                             final String category,
+                                                             final List<String> path,
+                                                             final Predicate<ConfiguredObject<?>> filterPredicate)
+    {
+        final ConfiguredObjectFinder finder = getConfiguredObjectFinder(root);
+        final Class<? extends ConfiguredObject> configuredClass = getRequestCategoryClass(category, root.getModel());
+        Collection<ConfiguredObject<?>> targetObjects =
+                finder.findObjectsFromPath(path, finder.getHierarchy(configuredClass), true);
+
+        if (targetObjects == null)
+        {
+            targetObjects = Collections.emptySet();
+        }
+        else if (filterPredicate != null)
+        {
+            targetObjects = targetObjects.stream().filter(filterPredicate).collect(Collectors.toList());
+        }
+        return targetObjects;
+    }
+
+    private RequestType getManagementRequestType(final String method,
+                                                 final String categoryName,
+                                                 final List<String> parts,
+                                                 final Class<? extends ConfiguredObject>[] hierarchy)
+    {
+        String servletPath = getCategoryMapping(categoryName);
+        if ("POST".equals(method))
+        {
+            return getPostRequestType(parts, hierarchy, servletPath);
+        }
+        else if ("PUT".equals(method))
+        {
+            return getPutRequestType(parts, hierarchy, servletPath);
+        }
+        else if ("GET".equals(method))
+        {
+            return getGetRequestType(parts, hierarchy, servletPath);
+        }
+        else if ("DELETE".equals(method))
+        {
+            return getDeleteRequestType(parts, hierarchy, servletPath);
+        }
+        else
+        {
+            throw createBadRequestManagementException(String.format("Unexpected method type '%s' for path '%s/%s'",
+                                                                    method,
+                                                                    servletPath,
+                                                                    String.join("/", parts)));
+        }
+    }
+
+    private RequestType getDeleteRequestType(final List<String> parts,
+                                             final Class<? extends ConfiguredObject>[] hierarchy,
+                                             final String servletPath)
+    {
+        if (parts.size() <= hierarchy.length)
+        {
+            return RequestType.MODEL_OBJECT;
+        }
+        else
+        {
+            if (USER_PREFERENCES.equals(parts.get(hierarchy.length)))
+            {
+                return RequestType.USER_PREFERENCES;
+            }
+        }
+        final String expectedPath = buildExpectedPath(servletPath, Arrays.asList(hierarchy));
+        throw createBadRequestManagementException(String.format(
+                "Invalid DELETE path '%s/%s'. Expected: '%s' or '%s/userpreferences[/<preference type>[/<preference name>]]'",
+                servletPath,
+                String.join("/", parts),
+                expectedPath,
+                expectedPath));
+    }
+
+    private RequestType getGetRequestType(final List<String> parts,
+                                          final Class<? extends ConfiguredObject>[] hierarchy,
+                                          final String servletPath)
+    {
+        if (parts.size() <= hierarchy.length)
+        {
+            return RequestType.MODEL_OBJECT;
+        }
+        else
+        {
+            if (USER_PREFERENCES.equals(parts.get(hierarchy.length)))
+            {
+                return RequestType.USER_PREFERENCES;
+            }
+            else if (VISIBLE_USER_PREFERENCES.equals(parts.get(hierarchy.length)))
+            {
+                return RequestType.VISIBLE_PREFERENCES;
+            }
+            else if (parts.size() == hierarchy.length + 1)
+            {
+                return RequestType.OPERATION;
+            }
+        }
+        final String expectedPath = buildExpectedPath(servletPath, Arrays.asList(hierarchy));
+        throw new IllegalArgumentException(String.format("Invalid GET path '%s/%s'. Expected: '%s[/<operation name>]'",
+                                                         servletPath,
+                                                         String.join("/", parts),
+                                                         expectedPath));
+    }
+
+    private RequestType getPutRequestType(final List<String> parts,
+                                          final Class<? extends ConfiguredObject>[] hierarchy,
+                                          final String servletPath)
+    {
+        if (parts.size() == hierarchy.length || parts.size() == hierarchy.length - 1)
+        {
+            return RequestType.MODEL_OBJECT;
+        }
+        else if (parts.size() > hierarchy.length && USER_PREFERENCES.equals(parts.get(hierarchy.length)))
+        {
+            return RequestType.USER_PREFERENCES;
+        }
+        else
+        {
+            final String expectedPath = buildExpectedPath(servletPath, Arrays.asList(hierarchy));
+            throw createBadRequestManagementException(String.format("Invalid PUT path '%s/%s'. Expected: '%s'",
+                                                                    servletPath,
+                                                                    String.join("/", parts),
+                                                                    expectedPath));
+        }
+    }
+
+    private RequestType getPostRequestType(final List<String> parts,
+                                           final Class<? extends ConfiguredObject>[] hierarchy,
+                                           final String servletPath)
+    {
+        if (parts.size() == hierarchy.length || parts.size() == hierarchy.length - 1)
+        {
+            return RequestType.MODEL_OBJECT;
+        }
+        else if (parts.size() > hierarchy.length)
+        {
+            if (USER_PREFERENCES.equals(parts.get(hierarchy.length)))
+            {
+                return RequestType.USER_PREFERENCES;
+            }
+            else if (parts.size() == hierarchy.length + 1
+                     && !VISIBLE_USER_PREFERENCES.equals(parts.get(hierarchy.length)))
+            {
+                return RequestType.OPERATION;
+            }
+        }
+        final List<Class<? extends ConfiguredObject>> hierarchyList = Arrays.asList(hierarchy);
+        final String expectedFullPath = buildExpectedPath(servletPath, hierarchyList);
+        final String expectedParentPath = buildExpectedPath(servletPath, hierarchyList.subList(0, hierarchy.length - 1));
+
+        throw createBadRequestManagementException(String.format(
+                "Invalid POST path '%s/%s'. Expected: '%s/<operation name>'"
+                + " or '%s'"
+                + " or '%s/userpreferences[/<preference type>]'",
+                servletPath,
+                String.join("/", parts),
+                expectedFullPath,
+                expectedParentPath,
+                expectedFullPath));
+    }
+
+    private Class<? extends ConfiguredObject> getRequestCategoryClass(final String categoryName,
+                                                                      final Model model)
+    {
+        for (Class<? extends ConfiguredObject> category : model.getSupportedCategories())
+        {
+            if (category.getSimpleName().toLowerCase().equals(categoryName.toLowerCase()))
+            {
+                return category;
+            }
+        }
+        throw createNotFoundManagementException(String.format("Category is not found for '%s'", categoryName));
+    }
+
+    private String buildExpectedPath(final String servletPath, final List<Class<? extends ConfiguredObject>> hierarchy)
+    {
+        final StringBuilder expectedPath = new StringBuilder(servletPath);
+        for (Class<? extends ConfiguredObject> part : hierarchy)
+        {
+            expectedPath.append("/<");
+            expectedPath.append(part.getSimpleName().toLowerCase());
+            expectedPath.append(" name>");
+        }
+        return expectedPath.toString();
+    }
+
+    private Predicate<ConfiguredObject<?>> buildFilterPredicates(final Map<String, List<String>> parameters)
+    {
+        return parameters.entrySet().stream()
+                  .filter(entry -> !RESERVED_PARAMS.contains(entry.getKey()))
+                  .map(entry -> {
+                      final String paramName = entry.getKey();
+                      final List<String> allowedValues = entry.getValue();
+                      return (Predicate<ConfiguredObject<?>>) object -> {
+                          Object value = object.getAttribute(paramName);
+                          return allowedValues.contains(String.valueOf(value));
+                      };
+                  }).reduce(Predicate::and).orElse(t -> true);
+    }
+
+    private ConfiguredObject<?> getTarget(final ConfiguredObject<?> root,
+                                          final String category,
+                                          final List<String> names)
+    {
+        final Class<? extends ConfiguredObject> configuredClass = getRequestCategoryClass(category, root.getModel());
+        final ConfiguredObject<?> target;
+        final ConfiguredObjectFinder finder = getConfiguredObjectFinder(root);
+        final Class<? extends ConfiguredObject>[] hierarchy = finder.getHierarchy(configuredClass);
+        if (names.isEmpty() && hierarchy.length == 0)
+        {
+            target = root;
+        }
+        else
+        {
+            ConfiguredObject theParent = root;
+            if (hierarchy.length > 1)
+            {
+                theParent = finder.findObjectParentsFromPath(names, hierarchy, configuredClass);
+            }
+            final String name = names.get(names.size() - 1);
+            target = theParent.getChildByName(configuredClass, name);
+            if (target == null)
+            {
+
+                final String errorMessage = String.format("%s '%s' not found",
+                                                          configuredClass.getSimpleName(),
+                                                          String.join("/", names));
+                throw createNotFoundManagementException(errorMessage);
+            }
+        }
+        return target;
+    }
+
+    private boolean hasFilter(Map<String, List<String>> parameters)
+    {
+        return parameters.keySet().stream().anyMatch(parameter -> !RESERVED_PARAMS.contains(parameter));
+    }
+
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerFactory.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerFactory.java
new file mode 100644
index 0000000..82ba07e
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerFactory.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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.management.plugin.controller.latest;
+
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementControllerFactory;
+import org.apache.qpid.server.model.BrokerModel;
+import org.apache.qpid.server.plugin.PluggableService;
+
+@PluggableService
+public class LatestManagementControllerFactory implements ManagementControllerFactory
+{
+    @Override
+    public String getType()
+    {
+        return "org.apache.qpid.server.management.plugin.model.latest";
+    }
+
+    @Override
+    public String getVersion()
+    {
+        return BrokerModel.MODEL_VERSION;
+    }
+
+    @Override
+    public String getPreviousVersion()
+    {
+        return "7.0";
+    }
+
+    @Override
+    public ManagementController createManagementController(final HttpManagementConfiguration<?> httpManagement,
+                                                           final ManagementController nextVersionManagementController)
+    {
+        return new LatestManagementController(httpManagement);
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java
index 69de651..cc67cd7 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfo.java
@@ -26,6 +26,8 @@ import java.util.Map;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
+import org.apache.qpid.server.management.plugin.RequestType;
+
 public class RequestInfo
 {
     private final RequestType _type;
@@ -124,8 +126,5 @@ public class RequestInfo
         return _hierarchySatisfied && !_hasWildcard;
     }
 
-    enum RequestType
-    {
-        OPERATION, USER_PREFERENCES, VISIBLE_PREFERENCES, MODEL_OBJECT
-    }
+
 }
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
index 4e5e524..9a53d11 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java
@@ -19,82 +19,51 @@
 
 package org.apache.qpid.server.management.plugin.servlet.rest;
 
-import static org.apache.qpid.server.management.plugin.HttpManagementConfiguration.DEFAULT_PREFERENCE_OPERATION_TIMEOUT;
-import static org.apache.qpid.server.management.plugin.HttpManagementConfiguration.PREFERENCE_OPERTAION_TIMEOUT_CONTEXT_NAME;
-import static org.apache.qpid.server.management.plugin.HttpManagementUtil.ensureFilenameIsRfc2183;
-import static org.apache.qpid.server.model.ConfiguredObjectTypeRegistry.getCollectionMemberType;
-import static org.apache.qpid.server.model.ConfiguredObjectTypeRegistry.returnsCollectionOfConfiguredObjects;
-
 import java.io.IOException;
-import java.lang.reflect.ParameterizedType;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.stream.Collectors;
 
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.Part;
 
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.google.common.base.Strings;
 
-import org.apache.qpid.server.configuration.IllegalConfigurationException;
 import org.apache.qpid.server.management.plugin.HttpManagementUtil;
-import org.apache.qpid.server.model.AbstractConfiguredObject;
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementRequest;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
 import org.apache.qpid.server.model.ConfiguredObject;
-import org.apache.qpid.server.model.ConfiguredObjectFinder;
-import org.apache.qpid.server.model.ConfiguredObjectOperation;
+import org.apache.qpid.server.model.ConfiguredObjectJacksonModule;
 import org.apache.qpid.server.model.Content;
-import org.apache.qpid.server.model.IllegalStateTransitionException;
-import org.apache.qpid.server.model.IntegrityViolationException;
-import org.apache.qpid.server.model.Model;
-import org.apache.qpid.server.model.OperationTimeoutException;
-import org.apache.qpid.server.model.preferences.UserPreferences;
+import org.apache.qpid.server.model.port.HttpPort;
 import org.apache.qpid.server.util.DataUrlUtils;
-import org.apache.qpid.server.util.ExternalServiceException;
-import org.apache.qpid.server.util.ExternalServiceTimeoutException;
-import org.apache.qpid.server.util.urlstreamhandler.data.Handler;
 
 public class RestServlet extends AbstractServlet
 {
     private static final long serialVersionUID = 1L;
+    private static final String APPLICATION_JSON = "application/json";
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(RestServlet.class);
-
-    public static final String DEPTH_PARAM = "depth";
-    public static final String OVERSIZE_PARAM = "oversize";
-    public static final String ACTUALS_PARAM = "actuals";
-    public static final String SORT_PARAM = "sort";
-    public static final String EXTRACT_INITIAL_CONFIG_PARAM = "extractInitialConfig";
-    public static final String EXCLUDE_INHERITED_CONTEXT_PARAM = "excludeInheritedContext";
-    private static final String SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST = "singletonModelObjectResponseAsList";
-    public static final Set<String> RESERVED_PARAMS =
-            new HashSet<>(Arrays.asList(DEPTH_PARAM,
-                                        SORT_PARAM,
-                                        OVERSIZE_PARAM,
-                                        ACTUALS_PARAM,
-                                        EXTRACT_INITIAL_CONFIG_PARAM,
-                                        CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM,
-                                        EXCLUDE_INHERITED_CONTEXT_PARAM,
-                                        SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST));
-    public static final int DEFAULT_DEPTH = 0;
-    public static final int DEFAULT_OVERSIZE = 120;
-
-    private transient final ConfiguredObjectToMapConverter _objectConverter = new ConfiguredObjectToMapConverter();
-    private transient RestUserPreferenceHandler _userPreferenceHandler;
+    private transient ManagementController _managementController;
 
     @SuppressWarnings("unused")
     public RestServlet()
@@ -107,505 +76,178 @@ public class RestServlet extends AbstractServlet
     {
         super.init();
 
-        Handler.register();
-        Long preferenceOperationTimeout = getManagementConfiguration().getContextValue(Long.class, PREFERENCE_OPERTAION_TIMEOUT_CONTEXT_NAME);
-        _userPreferenceHandler = new RestUserPreferenceHandler(preferenceOperationTimeout == null
-                                                                       ? DEFAULT_PREFERENCE_OPERATION_TIMEOUT
-                                                                       : preferenceOperationTimeout);
-    }
-
-
-    private Collection<ConfiguredObject<?>> getTargetObjects(final Class<? extends ConfiguredObject> configuredClass,
-                                                             final ConfiguredObjectFinder finder,
-                                                             RequestInfo requestInfo,
-                                                             List<Predicate<ConfiguredObject<?>>> filterPredicateList)
-    {
-        List<String> names = requestInfo.getModelParts();
+        final ServletConfig servletConfig = getServletConfig();
+        final ServletContext servletContext = servletConfig.getServletContext();
 
-        Collection<ConfiguredObject<?>> targetObjects = finder.findObjectsFromPath(names, finder.getHierarchy(configuredClass), true);
+        final String modelVersion = servletConfig.getInitParameter("qpid.controller.version");
+        if (modelVersion == null)
+        {
+            throw new ServletException("Controller version is not specified");
+        }
 
-        if (!(targetObjects == null || filterPredicateList.isEmpty()))
+        @SuppressWarnings("uncjecked")
+        ManagementController controller = (ManagementController) servletContext.getAttribute("qpid.controller.chain");
+        do
         {
-            Iterator<ConfiguredObject<?>> iter = targetObjects.iterator();
-            while (iter.hasNext())
+            if (controller.getVersion().equals(modelVersion))
             {
-                ConfiguredObject obj = iter.next();
-                for (Predicate<ConfiguredObject<?>> predicate : filterPredicateList)
-                {
-                    if (!predicate.apply(obj))
-                    {
-                        iter.remove();
-                        break;
-                    }
-                }
+                _managementController = controller;
+                break;
             }
-
+            controller = controller.getNextVersionManagementController();
         }
-        return targetObjects;
-    }
+        while (controller != null);
 
-    private List<Predicate<ConfiguredObject<?>>> buildFilterPredicates(final HttpServletRequest request)
-    {
-        List<Predicate<ConfiguredObject<?>>> predicates = new ArrayList<>();
-
-        for (final String paramName : Collections.list(request.getParameterNames()))
+        if (_managementController == null)
         {
-            if (!RESERVED_PARAMS.contains(paramName))
-            {
-                final List<String> allowedValues = Arrays.asList(request.getParameterValues(paramName));
-
-                predicates.add(new Predicate<ConfiguredObject<?>>()
-                {
-                    @Override
-                    public boolean apply(final ConfiguredObject<?> obj)
-                    {
-                        Object value = obj.getAttribute(paramName);
-                        return allowedValues.contains(String.valueOf(value));
-                    }
-                });
-            }
+            throw new ServletException("Controller is not found");
         }
-        return Collections.unmodifiableList(predicates);
     }
 
     @Override
-    protected void doGet(HttpServletRequest request,
-                         HttpServletResponse response,
+    protected void doGet(final HttpServletRequest httpServletRequest,
+                         final HttpServletResponse httpServletResponse,
                          final ConfiguredObject<?> managedObject)
-            throws ServletException, IOException
+            throws IOException
     {
-        ConfiguredObjectFinder finder = getConfiguredObjectFinder(managedObject);
-        Class<? extends ConfiguredObject> configuredClass = getConfiguredClass(request, managedObject);
-        if(configuredClass == null)
-        {
-            sendError(response, HttpServletResponse.SC_NOT_FOUND);
-            return;
-        }
-        final Class<? extends ConfiguredObject>[] hierarchy = finder.getHierarchy(configuredClass);
-        if(hierarchy == null)
-        {
-            sendError(response, HttpServletResponse.SC_NOT_FOUND);
-            return;
-        }
-
-        RequestInfoParser requestInfoParser = new RequestInfoParser(hierarchy);
-
-        RequestInfo requestInfo = requestInfoParser.parse(request);
-        switch (requestInfo.getType())
+        try
         {
-            case OPERATION:
-            {
-                doOperation(requestInfo, managedObject, configuredClass, finder, request, response);
-                break;
-            }
-            case MODEL_OBJECT:
-            {
-                // TODO - sort special params, everything else should act as a filter
-                String attachmentFilename = request.getParameter(CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM);
-
-                if (attachmentFilename != null)
-                {
-                    setContentDispositionHeaderIfNecessary(response, attachmentFilename);
-                }
+            final ManagementRequest request = new ServletManagementRequest(managedObject, httpServletRequest);
+            final ManagementController controller = getManagementController();
+            final ManagementResponse response = controller.handleGet(request);
 
-                List<Predicate<ConfiguredObject<?>>> filterPredicateList = buildFilterPredicates(request);
-                Collection<ConfiguredObject<?>> allObjects =
-                        getTargetObjects(configuredClass, finder, requestInfo, filterPredicateList);
-
-                boolean singleObjectRequest = requestInfo.isSingletonRequest() && filterPredicateList.isEmpty();
-
-                if (allObjects == null || (allObjects.isEmpty() && singleObjectRequest))
-                {
-                    sendJsonErrorResponse(request, response, HttpServletResponse.SC_NOT_FOUND, "Not Found");
-                    return;
-                }
-
-                int depth;
-                boolean actuals;
-                int oversizeThreshold;
-                boolean excludeInheritedContext;
-
-                depth = getIntParameterFromRequest(request, DEPTH_PARAM, DEFAULT_DEPTH);
-                oversizeThreshold = getIntParameterFromRequest(request, OVERSIZE_PARAM, DEFAULT_OVERSIZE);
-                actuals = getBooleanParameterFromRequest(request, ACTUALS_PARAM);
-                String excludeInheritedContextParameter = request.getParameter(EXCLUDE_INHERITED_CONTEXT_PARAM);
-
-                excludeInheritedContext = excludeInheritedContextParameter == null || Boolean.parseBoolean(
-                        excludeInheritedContextParameter);
-
-                boolean responseAsList = Boolean.parseBoolean(request.getParameter(SINGLETON_MODEL_OBJECT_RESPONSE_AS_LIST));
-                final Object responseObject;
-                if (!responseAsList && singleObjectRequest)
-                {
-                    if (allObjects.size() != 1)
-                    {
-                        throw new IllegalStateException(String.format(
-                                "Unexpected number of objects found [%d] for singleton request URI '%s'",
-                                allObjects.size(), request.getRequestURI()));
-                    }
-                    ConfiguredObject<?> singletonObject = allObjects.iterator().next();
-                    responseObject = _objectConverter.convertObjectToMap(singletonObject, configuredClass,
-                                                                         new ConfiguredObjectToMapConverter
-                                                                                 .ConverterOptions(
-                                                                                 depth,
-                                                                                 actuals,
-                                                                                 oversizeThreshold,
-                                                                                 request.isSecure(),
-                                                                                 excludeInheritedContext));
-                }
-                else
-                {
-                    final List<Object> outputList = new ArrayList<>();
-                    for (ConfiguredObject configuredObject : allObjects)
-                    {
-
-                        outputList.add(_objectConverter.convertObjectToMap(configuredObject, configuredClass,
-                                                                           new ConfiguredObjectToMapConverter.ConverterOptions(
-                                                                                   depth,
-                                                                                   actuals,
-                                                                                   oversizeThreshold,
-                                                                                   request.isSecure(),
-                                                                                   excludeInheritedContext)));
-                    }
-
-                    responseObject = outputList;
-                }
-
-                boolean sendCachingHeaders = attachmentFilename == null;
-                sendJsonResponse(responseObject,
-                                 request,
-                                 response,
-                                 HttpServletResponse.SC_OK,
-                                 sendCachingHeaders);
-                break;
-            }
-            case VISIBLE_PREFERENCES:
-            case USER_PREFERENCES:
-            {
-                doGetUserPreferences(managedObject, configuredClass, finder, requestInfo, request, response);
-                break;
-            }
-
-            default:
-            {
-                throw new IllegalStateException(String.format("Unexpected request type '%s' for path '%s'",
-                                                              requestInfo.getType(),
-                                                              request.getPathInfo()));
-            }
-        }
-    }
-
-
-    private boolean isSingleObjectRequest(final RequestInfo requestInfo,
-                                          final Class<? extends ConfiguredObject>[] hierarchy)
-    {
-        if (hierarchy.length > 0)
-        {
-            List<String> pathInfoElements = requestInfo.getModelParts();
-            return pathInfoElements.size() == hierarchy.length;
+            sendResponse(request, response, httpServletRequest, httpServletResponse, controller);
         }
-
-        return false;
-    }
-
-    private Class<? extends ConfiguredObject> getConfiguredClass(HttpServletRequest request, ConfiguredObject<?> managedObject)
-    {
-        final String[] servletPathElements = request.getServletPath().split("/");
-        String categoryName = servletPathElements[servletPathElements.length-1];
-        Model model = managedObject.getModel();
-        for(Class<? extends ConfiguredObject> category : model.getSupportedCategories())
+        catch (ManagementException e)
         {
-            if(category.getSimpleName().toLowerCase().equals(categoryName))
-            {
-                return category;
-            }
+            sendResponse(e, httpServletRequest, httpServletResponse);
         }
-        return null;
-    }
-
-    @Override
-    protected void doPut(HttpServletRequest request,
-                         HttpServletResponse response,
-                         final ConfiguredObject<?> managedObject)
-            throws ServletException, IOException
-    {
-        performCreateOrUpdate(request, response, managedObject);
     }
 
     @Override
-    protected void service(HttpServletRequest request, HttpServletResponse response)
-            throws ServletException, IOException
+    protected void doPost(final HttpServletRequest httpServletRequest,
+                          final HttpServletResponse httpServletResponse,
+                          final ConfiguredObject<?> managedObject) throws IOException
     {
         try
         {
-            super.service(request, response);
+            final ManagementRequest request = new ServletManagementRequest(managedObject, httpServletRequest);
+            final ManagementController controller = getManagementController();
+            final ManagementResponse response = controller.handlePost(request);
+
+            sendResponse(request, response, httpServletRequest, httpServletResponse, controller);
         }
-        catch (Exception | NoClassDefFoundError e)
+        catch (ManagementException e)
         {
-            setResponseStatus(request, response, e);
+            sendResponse(e, httpServletRequest, httpServletResponse);
         }
     }
 
-    private void performCreateOrUpdate(HttpServletRequest request,
-                                       HttpServletResponse response,
-                                       final ConfiguredObject<?> managedObject)
-            throws IOException, ServletException
+    @Override
+    protected void doPut(final HttpServletRequest httpServletRequest,
+                         final HttpServletResponse httpServletResponse,
+                         final ConfiguredObject<?> managedObject) throws IOException
     {
-
-        ConfiguredObjectFinder finder = getConfiguredObjectFinder(managedObject);
-        final Class<? extends ConfiguredObject> configuredClass = getConfiguredClass(request, managedObject);
-        final Class<? extends ConfiguredObject>[] hierarchy = finder.getHierarchy(configuredClass);
-        RequestInfoParser requestInfoParser = new RequestInfoParser(hierarchy);
-
-        response.setContentType("application/json");
-
-        RequestInfo requestInfo = requestInfoParser.parse(request);
-        switch (requestInfo.getType())
+        try
         {
-            case MODEL_OBJECT:
-            {
-                List<String> names = requestInfo.getModelParts();
-                boolean isFullObjectURL = names.size() == hierarchy.length;
-                Map<String, Object> providedObject = getRequestProvidedObject(request, requestInfo);
-                if (names.isEmpty() && hierarchy.length == 0)
-                {
-                    managedObject.setAttributes(providedObject);
-                    response.setStatus(HttpServletResponse.SC_OK);
-                    return;
-                }
-
-                ConfiguredObject theParent = managedObject;
-                Class<? extends ConfiguredObject> objClass = configuredClass;
-                if (hierarchy.length > 1)
-                {
+            final ManagementRequest request = new ServletManagementRequest(managedObject, httpServletRequest);
+            final ManagementController controller = getManagementController();
+            final ManagementResponse response = controller.handlePut(request);
 
-                    theParent = finder.findObjectParentsFromPath(names, hierarchy, configuredClass);
-                }
-
-                if (isFullObjectURL)
-                {
-                    String name = names.get(names.size() - 1);
-                    ConfiguredObject<?> configuredObject = theParent.getChildByName(objClass, name);
-
-                    if (configuredObject != null)
-                    {
-                        configuredObject.setAttributes(providedObject);
-                        response.setStatus(HttpServletResponse.SC_OK);
-                        return;
-                    }
-                    else if ("POST".equalsIgnoreCase(request.getMethod()))
-                    {
-                        sendJsonErrorResponse(request, response,
-                                              HttpServletResponse.SC_NOT_FOUND,
-                                              String.format("%s '%s' not found", configuredClass.getSimpleName(), name));
-                        return;
-                    }
-                    else
-                    {
-                        providedObject.put(ConfiguredObject.NAME, name);
-                    }
-                }
-
-                ConfiguredObject<?> configuredObject = theParent.createChild(objClass, providedObject);
-                StringBuffer requestURL = request.getRequestURL();
-                if (!isFullObjectURL)
-                {
-                    requestURL.append("/").append(configuredObject.getName());
-                }
-                response.setHeader("Location", requestURL.toString());
-                response.setStatus(HttpServletResponse.SC_CREATED);
-                break;
-            }
-            case OPERATION:
-            {
-                doOperation(requestInfo, managedObject, configuredClass, finder, request, response);
-                break;
-            }
-            case USER_PREFERENCES:
-            {
-                doPostOrPutUserPreference(requestInfo, managedObject, configuredClass, finder, request, response);
-                break;
-            }
-            default:
-            {
-                throw new IllegalStateException(String.format("Unexpected request type '%s' for path '%s'",
-                                                              requestInfo.getType(),
-                                                              request.getPathInfo()));
-            }
+            sendResponse(request, response, httpServletRequest, httpServletResponse, controller);
+        }
+        catch (ManagementException e)
+        {
+            sendResponse(e, httpServletRequest, httpServletResponse);
         }
     }
 
-    private void doGetUserPreferences(final ConfiguredObject<?> managedObject,
-                                      final Class<? extends ConfiguredObject> configuredClass,
-                                      final ConfiguredObjectFinder finder, final RequestInfo requestInfo,
-                                      final HttpServletRequest request,
-                                      final HttpServletResponse response) throws IOException, ServletException
+    @Override
+    protected void doDelete(final HttpServletRequest httpServletRequest,
+                            final HttpServletResponse httpServletResponse,
+                            final ConfiguredObject<?> managedObject) throws IOException
     {
-        Collection<ConfiguredObject<?>> allObjects = getTargetObjects(
-                configuredClass,
-                                                                      finder,
-                                                                      requestInfo,
-                                                                      Collections.<Predicate<ConfiguredObject<?>>>emptyList());
-
-        if (allObjects == null || (allObjects.isEmpty() && isSingleObjectRequest(requestInfo, finder.getHierarchy(configuredClass))))
+        try
         {
-            sendJsonErrorResponse(request, response, HttpServletResponse.SC_NOT_FOUND, "Not Found");
-            return;
-        }
+            final ManagementRequest request = new ServletManagementRequest(managedObject, httpServletRequest);
+            final ManagementController controller = getManagementController();
+            final ManagementResponse response = controller.handleDelete(request);
 
-        final Object responseObject;
-        if (requestInfo.hasWildcard())
-        {
-            responseObject = new ArrayList<>(allObjects.size());
-            for (ConfiguredObject<?> target : allObjects)
-            {
-                final UserPreferences userPreferences = target.getUserPreferences();
-                try
-                {
-                    final Object preferences = _userPreferenceHandler.handleGET(userPreferences, requestInfo);
-                    if (preferences == null || (preferences instanceof Collection
-                                                && ((Collection) preferences).isEmpty()) || (preferences instanceof Map
-                                                                                             && ((Map) preferences).isEmpty()))
-                    {
-                        continue;
-                    }
-                    ((List<Object>) responseObject).add(preferences);
-                }
-                catch (NotFoundException e)
-                {
-                    // The case where the preference's type and name is provided, but this particular object does not
-                    // have a matching preference.
-                }
-            }
+            sendResponse(request, response, httpServletRequest, httpServletResponse, controller);
         }
-        else
+        catch (ManagementException e)
         {
-            ConfiguredObject<?> target = allObjects.iterator().next();
-            final UserPreferences userPreferences = target.getUserPreferences();
-
-            responseObject = _userPreferenceHandler.handleGET(userPreferences, requestInfo);
+            sendResponse(e, httpServletRequest, httpServletResponse);
         }
-        sendJsonResponse(responseObject, request, response);
     }
 
-    private void doPostOrPutUserPreference(final RequestInfo requestInfo,
-                                           final ConfiguredObject<?> managedObject,
-                                           final Class<? extends ConfiguredObject> configuredClass,
-                                           final ConfiguredObjectFinder finder,
-                                           final HttpServletRequest request,
-                                           final HttpServletResponse response) throws IOException, ServletException
+    private ManagementController getManagementController()
     {
-        ConfiguredObject<?> target = getTarget(requestInfo, managedObject, configuredClass, finder);
+        return _managementController;
+    }
 
-        final Object providedObject = getRequestProvidedObject(request, requestInfo, Object.class);
-        if ("POST".equals(request.getMethod()))
-        {
-            _userPreferenceHandler.handlePOST(target, requestInfo, providedObject);
-        }
-        else if ("PUT".equals(request.getMethod()))
-        {
-            _userPreferenceHandler.handlePUT(target, requestInfo, providedObject);
-        }
-        else
-        {
-            sendJsonErrorResponse(request,
-                                  response,
-                                  HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
-                                  "unexpected http method");
-        }
+    private void sendResponse(final ManagementException managementException,
+                              final HttpServletRequest request,
+                              final HttpServletResponse response) throws IOException
+    {
+        setHeaders(response);
+        setExceptionHeaders(managementException, response);
+        response.setStatus(managementException.getStatusCode());
+        writeJsonResponse(Collections.singletonMap("errorMessage", managementException.getMessage()),
+                          request,
+                          response);
     }
 
-    private void doOperation(final RequestInfo requestInfo,
-                             final ConfiguredObject<?> managedObject,
-                             final Class<? extends ConfiguredObject> configuredClass,
-                             final ConfiguredObjectFinder finder,
-                             final HttpServletRequest request,
-                             final HttpServletResponse response) throws IOException, ServletException
+    private void setExceptionHeaders(final ManagementException managementException, final HttpServletResponse response)
     {
-        ConfiguredObject<?> target = getTarget(requestInfo, managedObject, configuredClass, finder);
-        if (target == null)
+        Map<String, String> headers = managementException.getHeaders();
+        if (headers != null)
         {
-            return;
+            headers.forEach(response::setHeader);
         }
-        String operationName = requestInfo.getOperationName();
-        final Map<String, ConfiguredObjectOperation<?>> availableOperations =
-                managedObject.getModel().getTypeRegistry().getOperations(target.getClass());
-        ConfiguredObjectOperation operation = availableOperations.get(operationName);
-        Map<String, Object> operationArguments;
-
+    }
 
-        String requestMethod = request.getMethod();
-        if (operation == null)
+    private String toContentDispositionHeader(final String attachmentFilename)
+    {
+        String filenameRfc2183 = HttpManagementUtil.ensureFilenameIsRfc2183(attachmentFilename);
+        if (filenameRfc2183.length() > 0)
         {
-            sendJsonErrorResponse(request,
-                                  response,
-                                  HttpServletResponse.SC_NOT_FOUND,
-                                  "No such operation: " + operationName);
-            return;
+            return String.format("attachment; filename=\"%s\"", filenameRfc2183);
         }
         else
         {
-            switch (requestMethod)
-            {
-                case "GET":
-                    if (operation.isNonModifying())
-                    {
-                        operationArguments = getOperationArgumentsAsMap(request);
-                        operationArguments.keySet().removeAll(Arrays.asList(CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM));
-                    }
-                    else
-                    {
-                        response.addHeader("Allow", "POST");
-                        sendJsonErrorResponse(request,
-                                              response,
-                                              HttpServletResponse.SC_METHOD_NOT_ALLOWED,
-                                              "Operation "
-                                              + operationName
-                                              + " modifies the object so you must use POST.");
-                        return;
-                    }
-
-                    break;
-                case "POST":
-                    operationArguments = getRequestProvidedObject(request, requestInfo);
-                    break;
-                default:
-                    response.addHeader("Allow", (operation.isNonModifying() ? "POST, GET" : "POST"));
-                    sendJsonErrorResponse(request,
-                                          response,
-                                          HttpServletResponse.SC_METHOD_NOT_ALLOWED,
-                                          "Operation "
-                                          + operationName
-                                          + " does not support the "
-                                          + requestMethod
-                                          + " requestMethod.");
-                    return;
-            }
+            // Agent will allow user to choose a name
+            return "attachment";
         }
+    }
 
-
-        if(operation.isSecure(target, operationArguments) && !(request.isSecure() || HttpManagementUtil.getPort(request).isAllowConfidentialOperationsOnInsecureChannels()))
+    private void sendResponse(final ManagementRequest managementRequest,
+                              final ManagementResponse managementResponse,
+                              final HttpServletRequest request,
+                              final HttpServletResponse response,
+                              final ManagementController controller) throws IOException
+    {
+        setHeaders(response);
+        Map<String, String> headers = managementResponse.getHeaders();
+        if (!headers.isEmpty())
         {
-            sendJsonErrorResponse(request,
-                                  response,
-                                  HttpServletResponse.SC_FORBIDDEN,
-                                  "Operation '" + operationName + "' can only be performed over a secure (HTTPS) connection");
-            return;
+            headers.forEach(response::setHeader);
         }
 
-        Object returnVal = operation.perform(target, operationArguments);
-        String attachmentFilename = request.getParameter(CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM);
-        if (attachmentFilename != null)
+        Map<String, List<String>> parameters = managementRequest.getParameters();
+        if (parameters.containsKey(CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM))
         {
-            setContentDispositionHeaderIfNecessary(response, attachmentFilename);
+            String attachmentFilename = managementRequest.getParameter(CONTENT_DISPOSITION_ATTACHMENT_FILENAME_PARAM);
+            response.setHeader(CONTENT_DISPOSITION, toContentDispositionHeader(attachmentFilename));
         }
+        response.setStatus(managementResponse.getResponseCode());
 
-        if(returnVal instanceof Content)
+        Object body = managementResponse.getBody();
+        if (body instanceof Content)
         {
-            Content content = (Content)returnVal;
+            Content content = (Content) body;
             try
             {
-
                 writeTypedContent(content, request, response);
             }
             finally
@@ -613,350 +255,242 @@ public class RestServlet extends AbstractServlet
                 content.release();
             }
         }
-        else
+        else if (body != null)
         {
-            final ConfiguredObjectToMapConverter.ConverterOptions converterOptions =
-                    new ConfiguredObjectToMapConverter.ConverterOptions(DEFAULT_DEPTH,
-                                                                        false,
-                                                                        DEFAULT_OVERSIZE,
-                                                                        request.isSecure(),
-                                                                        true);
-            if (ConfiguredObject.class.isAssignableFrom(operation.getReturnType()))
+            response.setContentType(APPLICATION_JSON);
+            Object data;
+            if (managementResponse.getType() == ResponseType.MODEL_OBJECT)
             {
-                returnVal = _objectConverter.convertObjectToMap((ConfiguredObject<?>) returnVal,
-                                                                operation.getReturnType(),
-                                                                converterOptions);
+                data = controller.formatConfiguredObject(
+                        managementResponse.getBody(),
+                        parameters,
+                        managementRequest.isSecure()
+                        || managementRequest.isConfidentialOperationAllowedOnInsecureChannel());
             }
-            else if (returnsCollectionOfConfiguredObjects(operation))
+            else
             {
-                List<Map<String, Object>> output = new ArrayList<>();
-                for (Object configuredObject : (Collection)returnVal)
-                {
-                    output.add(_objectConverter.convertObjectToMap((ConfiguredObject<?>) configuredObject,
-                                                                   getCollectionMemberType((ParameterizedType) operation.getGenericReturnType()),
-                                                                   converterOptions));
-                }
-                returnVal = output;
+                data = managementResponse.getBody();
             }
-            sendJsonResponse(returnVal, request, response);
+            writeJsonResponse(data, request, response);
         }
     }
 
-    private ConfiguredObject<?> getTarget(final RequestInfo requestInfo,
-                                          final ConfiguredObject<?> managedObject,
-                                          final Class<? extends ConfiguredObject> configuredClass,
-                                          final ConfiguredObjectFinder finder) throws IOException
+    private void setHeaders(final HttpServletResponse response)
     {
-        final ConfiguredObject<?> target;
-        final List<String> names = requestInfo.getModelParts();
-        final Class<? extends ConfiguredObject>[] hierarchy = finder.getHierarchy(configuredClass);
-        if (names.isEmpty() && hierarchy.length == 0)
+        response.setHeader("Cache-Control", "no-cache");
+        response.setHeader("Pragma", "no-cache");
+        response.setDateHeader("Expires", 0);
+        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+    }
+
+    private void writeJsonResponse(final Object formattedResponse,
+                                   final HttpServletRequest request,
+                                   final HttpServletResponse response) throws IOException
+    {
+        try (OutputStream stream = HttpManagementUtil.getOutputStream(request,
+                                                                      response,
+                                                                      getManagementConfiguration()))
         {
-            target = managedObject;
+            ObjectMapper mapper = ConfiguredObjectJacksonModule.newObjectMapper(false);
+            mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
+            mapper.writeValue(stream, formattedResponse);
         }
-        else
+    }
+
+    private static Map<String, List<String>> parseQueryString(String queryString)
+    {
+        if (Strings.isNullOrEmpty(queryString))
         {
-            ConfiguredObject theParent = managedObject;
-            if (hierarchy.length > 1)
+            return Collections.emptyMap();
+        }
+        Map<String, List<String>> query = new LinkedHashMap<>();
+        final String[] pairs = queryString.split("&");
+        for (String pairString : pairs)
+        {
+            List<String> pair = new ArrayList<>(Arrays.asList(pairString.split("=")));
+            if (pair.size() == 1)
             {
-
-                ConfiguredObject parent =
-                        finder.findObjectParentsFromPath(names, hierarchy, configuredClass);
-                theParent = parent;
+                pair.add(null);
             }
-            String name = names.get(names.size() - 1);
-            target = theParent.getChildByName(configuredClass, name);
-            if (target == null)
+            else if (pair.size() != 2)
             {
+                throw new IllegalArgumentException(String.format("could not parse query string '%s'", queryString));
+            }
 
-                final String errorMessage = String.format("%s '%s' not found",
-                                                          configuredClass.getSimpleName(),
-                                                          Joiner.on("/").join(names));
-                throw new NotFoundException(errorMessage);
+            String key;
+            String value;
+            try
+            {
+                key = URLDecoder.decode(pair.get(0), "UTF-8");
+                value = pair.get(1) == null ? null : URLDecoder.decode(pair.get(1), "UTF-8");
+            }
+            catch (UnsupportedEncodingException e)
+            {
+                throw new RuntimeException(e);
+            }
+            if (!query.containsKey(key))
+            {
+                query.put(key, new ArrayList<>());
             }
+            query.get(key).add(value);
         }
-        return target;
+        return query;
     }
 
-    private Map<String, Object> getOperationArgumentsAsMap(HttpServletRequest request)
+    private static class ServletManagementRequest implements ManagementRequest
     {
-        Map<String, Object> providedObject;
-        providedObject = new HashMap<>();
-        for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet())
+        private final HttpPort<?> _port;
+        private final HttpServletRequest _request;
+        private final Map<String, List<String>> _query;
+        private final List<String> _path;
+        private final ConfiguredObject<?> _root;
+        private final String _category;
+        private final Map<String, String> _headers;
+
+        ServletManagementRequest(final ConfiguredObject<?> root,
+                                 final HttpServletRequest request)
         {
-            String[] value = entry.getValue();
-            if (value != null)
-            {
-                if(value.length > 1)
-                {
-                    providedObject.put(entry.getKey(), Arrays.asList(value));
-                }
-                else
-                {
-                    providedObject.put(entry.getKey(), value[0]);
-                }
-            }
+            _root = root;
+            _request = request;
+            _port = HttpManagementUtil.getPort(request);
+            _query = Collections.unmodifiableMap(parseQueryString(request.getQueryString()));
+            String pathInfo = _request.getPathInfo() == null ? "" : _request.getPathInfo();
+            String servletPath = request.getServletPath();
+            _path = Collections.unmodifiableList(HttpManagementUtil.getPathInfoElements(servletPath, pathInfo));
+            final String[] servletPathElements = servletPath.split("/");
+            _category = servletPathElements[servletPathElements.length - 1];
+            final Map<String, String> headers = Collections.list(request.getHeaderNames())
+                                                           .stream()
+                                                           .collect(Collectors.toMap(name -> name, request::getHeader));
+            _headers = Collections.unmodifiableMap(headers);
         }
-        return providedObject;
-    }
 
-    private Map<String, Object> getRequestProvidedObject(HttpServletRequest request, final RequestInfo requestInfo)
-            throws IOException, ServletException
-    {
-        return getRequestProvidedObject(request, requestInfo, LinkedHashMap.class);
-    }
+        public ConfiguredObject<?> getRoot()
+        {
+            return _root;
+        }
 
-    private <T> T getRequestProvidedObject(HttpServletRequest request,
-                                           final RequestInfo requestInfo,
-                                           Class<T> expectedClass)
-            throws IOException, ServletException
-    {
-        T providedObject;
+        public boolean isSecure()
+        {
+            return _request.isSecure();
+        }
 
-        ArrayList<String> headers = Collections.list(request.getHeaderNames());
-        ObjectMapper mapper = new ObjectMapper();
+        public boolean isConfidentialOperationAllowedOnInsecureChannel()
+        {
+            return _port.isAllowConfidentialOperationsOnInsecureChannels();
+        }
 
-        if (headers.contains("Content-Type") && request.getHeader("Content-Type").startsWith("multipart/form-data"))
+        public List<String> getPath()
         {
-            providedObject = (T) new LinkedHashMap<>();
-            Map<String, String> fileUploads = new HashMap<>();
-            Collection<Part> parts = request.getParts();
-            for (Part part : parts)
-            {
-                if ("data".equals(part.getName()) && "application/json".equals(part.getContentType()))
-                {
-                    try
-                    {
-                        providedObject = (T) mapper.readValue(part.getInputStream(), LinkedHashMap.class);
-                    }
-                    catch (JsonProcessingException e)
-                    {
-                        throw new IllegalArgumentException("Cannot parse the operation body as json",e);
-                    }
+            return _path;
+        }
 
-                }
-                else
-                {
-                    byte[] data = new byte[(int) part.getSize()];
-                    part.getInputStream().read(data);
-                    String inlineURL = DataUrlUtils.getDataUrlForBytes(data);
-                    fileUploads.put(part.getName(), inlineURL);
-                }
-            }
-            ((Map<String, Object>) providedObject).putAll(fileUploads);
+        public String getMethod()
+        {
+            return _request.getMethod();
         }
-        else
+
+        public Map<String, List<String>> getParameters()
         {
-            try
-            {
-                providedObject = mapper.readValue(request.getInputStream(), expectedClass);
-            }
-            catch (JsonProcessingException e)
-            {
-                throw new IllegalArgumentException("Cannot parse the operation body as json",e);
-            }
+            return Collections.unmodifiableMap(_query);
         }
-        return providedObject;
-    }
 
-    private void setResponseStatus(HttpServletRequest request, HttpServletResponse response, Throwable e)
-            throws IOException
-    {
-        if (e instanceof SecurityException)
+        @Override
+        public String getParameter(final String name)
         {
-            LOGGER.debug("{}, sending {}", e.getClass().getName(), HttpServletResponse.SC_FORBIDDEN, e);
-            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            final List<String> values = _query.get(name);
+            return values == null || values.isEmpty() ? null : values.get(0);
         }
-        else
+
+        public Map<String, String> getHeaders()
         {
-            int responseCode = HttpServletResponse.SC_BAD_REQUEST;
-            String message = e.getMessage();
-            if (e instanceof AbstractConfiguredObject.DuplicateIdException
-                || e instanceof AbstractConfiguredObject.DuplicateNameException
-                || e instanceof IntegrityViolationException
-                || e instanceof IllegalStateTransitionException)
-            {
-                responseCode = HttpServletResponse.SC_CONFLICT;
-            }
-            else if (e instanceof NotFoundException)
-            {
-                if (LOGGER.isTraceEnabled())
-                {
-                    LOGGER.trace(e.getClass().getSimpleName() + " processing request", e);
-                }
-                responseCode = HttpServletResponse.SC_NOT_FOUND;
-            }
-            else if (e instanceof IllegalConfigurationException || e instanceof IllegalArgumentException)
-            {
-                LOGGER.warn("{} processing request {} from user '{}': {}",
-                            e.getClass().getSimpleName(),
-                            HttpManagementUtil.getRequestURL(request),
-                            HttpManagementUtil.getRequestPrincipals(request),
-                            message);
-                Throwable t = e;
-                int maxDepth = 10;
-                while ((t = t.getCause()) != null && maxDepth-- != 0)
-                {
-                    LOGGER.warn("... caused by " + t.getClass().getSimpleName() + "  : " + t.getMessage());
-                }
-                if (LOGGER.isDebugEnabled())
-                {
-                    LOGGER.debug(e.getClass().getSimpleName() + " processing request", e);
-                }
-                responseCode = SC_UNPROCESSABLE_ENTITY;
-            }
-            else if (e instanceof OperationTimeoutException)
-            {
-                message = "Timeout occurred";
-                if (LOGGER.isDebugEnabled())
-                {
-                    LOGGER.debug("Timeout during processing of request {} from user '{}'",
-                                 HttpManagementUtil.getRequestURL(request),
-                                 HttpManagementUtil.getRequestPrincipals(request),
-                                 e);
-                }
-                else
-                {
-                    LOGGER.info("Timeout during processing of request {} from user '{}'",
-                            HttpManagementUtil.getRequestURL(request),
-                            HttpManagementUtil.getRequestPrincipals(request));
-                }
+            return _headers;
+        }
 
-                responseCode = HttpServletResponse.SC_BAD_GATEWAY;
-            }
-            else if (e instanceof NoClassDefFoundError)
-            {
-                message = "Not found: " + message;
-                LOGGER.warn("Unexpected exception processing request ", e);
-            }
-            else if (e instanceof ExternalServiceTimeoutException)
-            {
-                responseCode = HttpServletResponse.SC_GATEWAY_TIMEOUT;
-                LOGGER.warn("External request timeout ", e);
-            }
-            else if (e instanceof ExternalServiceException)
+        @SuppressWarnings("unchecked")
+        public <T> T getBody(Class<T> type)
+        {
+            try
             {
-                responseCode = HttpServletResponse.SC_BAD_GATEWAY;
-                LOGGER.warn("External request failed ", e);
+                return parse(type);
             }
-            else
+            catch (IOException | ServletException e)
             {
-                // This should not happen
-                if (e instanceof RuntimeException)
-                {
-                    throw (RuntimeException) e;
-                }
-                else if (e instanceof Error)
-                {
-                    throw (Error) e;
-                }
-                else
-                {
-                    throw new RuntimeException("Unexpected Exception", e);
-                }
+                throw ManagementException.createBadRequestManagementException("Cannot parse body", e);
             }
-
-            sendJsonErrorResponse(request, response, responseCode, message);
         }
-    }
 
-    @Override
-    protected void doDelete(HttpServletRequest request,
-                            HttpServletResponse response,
-                            final ConfiguredObject<?> managedObject) throws ServletException, IOException
-    {
-        ConfiguredObjectFinder finder = getConfiguredObjectFinder(managedObject);
-        Class<? extends ConfiguredObject> configuredClass = getConfiguredClass(request, managedObject);
-        final Class<? extends ConfiguredObject>[] hierarchy = finder.getHierarchy(configuredClass);
-        RequestInfoParser requestInfoParser = new RequestInfoParser(hierarchy);
-
-        RequestInfo requestInfo = requestInfoParser.parse(request);
-
-        Collection<ConfiguredObject<?>> allObjects = getTargetObjects(configuredClass, finder, requestInfo, buildFilterPredicates(request));
-        if (allObjects == null)
+        @Override
+        public String getRequestURL()
         {
-            throw new NotFoundException("Not Found");
+            return _request.getRequestURL().toString();
         }
 
-        switch (requestInfo.getType())
+        @SuppressWarnings("unchecked")
+        private <T> T parse(Class<T> type) throws IOException, ServletException
         {
-            case MODEL_OBJECT:
-            {
-                for (ConfiguredObject o : allObjects)
-                {
-                    o.delete();
-                }
+            T providedObject;
+            final ObjectMapper mapper = new ObjectMapper();
 
-                sendCachingHeadersOnResponse(response);
-                response.setStatus(HttpServletResponse.SC_OK);
-                break;
-            }
-            case USER_PREFERENCES:
+            if (_headers.containsKey("Content-Type") && _request.getHeader("Content-Type")
+                                                                .startsWith("multipart/form-data"))
             {
-                //TODO: define format how to report the results for bulk delete, i.e. how to report individual errors and success statuses
-                if (allObjects.size() > 1)
-                {
-                    sendJsonErrorResponse(request,
-                                          response,
-                                          HttpServletResponse.SC_BAD_REQUEST,
-                                          "Deletion of user preferences using wildcards is unsupported");
-                    return;
-                }
-                for (ConfiguredObject o : allObjects)
+                Map<String, Object> items = new LinkedHashMap<>();
+                Map<String, String> fileUploads = new HashMap<>();
+                Collection<Part> parts = _request.getParts();
+                for (Part part : parts)
                 {
-                    _userPreferenceHandler.handleDELETE(o.getUserPreferences(), requestInfo);
+                    if ("data".equals(part.getName()) && "application/json".equals(part.getContentType()))
+                    {
+                        items = mapper.readValue(part.getInputStream(), LinkedHashMap.class);
+                    }
+                    else
+                    {
+                        byte[] data = new byte[(int) part.getSize()];
+                        try (InputStream inputStream = part.getInputStream())
+                        {
+                            inputStream.read(data);
+                        }
+                        fileUploads.put(part.getName(), DataUrlUtils.getDataUrlForBytes(data));
+                    }
                 }
-                break;
-            }
+                items.putAll(fileUploads);
 
-            default:
+                providedObject = (T) items;
+            }
+            else
             {
-                sendJsonErrorResponse(request, response, HttpServletResponse.SC_BAD_REQUEST, "Unsupported delete call");
+                providedObject = mapper.readValue(_request.getInputStream(), type);
             }
+            return providedObject;
         }
-    }
 
-    @Override
-    protected void doPost(HttpServletRequest request,
-                          HttpServletResponse response,
-                          final ConfiguredObject<?> managedObject) throws ServletException, IOException
-    {
-        performCreateOrUpdate(request, response, managedObject);
-    }
-
-
-    private int getIntParameterFromRequest(final HttpServletRequest request,
-                                           final String paramName,
-                                           final int defaultValue)
-    {
-        int intValue = defaultValue;
-        final String stringValue = request.getParameter(paramName);
-        if(stringValue!=null)
+        @Override
+        public Map<String, Object> getParametersAsFlatMap()
         {
-            try
+            final Map<String, Object> providedObject = new HashMap<>();
+            for (Map.Entry<String, List<String>> entry : _query.entrySet())
             {
-                intValue = Integer.parseInt(stringValue);
-            }
-            catch (NumberFormatException e)
-            {
-                LOGGER.warn("Could not parse " + stringValue + " as integer for parameter " + paramName);
+                final List<String> value = entry.getValue();
+                if (value != null)
+                {
+                    if (value.size() == 1)
+                    {
+                        providedObject.put(entry.getKey(), value.get(0));
+                    }
+                    else
+                    {
+                        providedObject.put(entry.getKey(), value);
+                    }
+                }
             }
+            return providedObject;
         }
-        return intValue;
-    }
 
-    private boolean getBooleanParameterFromRequest(HttpServletRequest request, final String paramName)
-    {
-        return getBooleanParameterFromRequest(request, paramName, false);
-    }
-
-    private boolean getBooleanParameterFromRequest(HttpServletRequest request, final String paramName, final boolean defaultValue)
-    {
-        String value = request.getParameter(paramName);
-        if (value == null)
+        @Override
+        public String getCategory()
         {
-            return defaultValue;
+            return _category;
         }
-        return Boolean.parseBoolean(value);
     }
 }
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestUserPreferenceHandler.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestUserPreferenceHandler.java
index a0d4113..50c506c 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestUserPreferenceHandler.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestUserPreferenceHandler.java
@@ -19,12 +19,8 @@
 
 package org.apache.qpid.server.management.plugin.servlet.rest;
 
-import java.security.AccessController;
-import java.security.Principal;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -33,16 +29,15 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
-import javax.security.auth.Subject;
-
 import com.google.common.base.Joiner;
 import com.google.common.util.concurrent.ListenableFuture;
 
-import org.apache.qpid.server.util.FutureHelper;
+import org.apache.qpid.server.management.plugin.RequestType;
 import org.apache.qpid.server.model.ConfiguredObject;
 import org.apache.qpid.server.model.preferences.Preference;
 import org.apache.qpid.server.model.preferences.PreferenceFactory;
 import org.apache.qpid.server.model.preferences.UserPreferences;
+import org.apache.qpid.server.util.FutureHelper;
 
 public class RestUserPreferenceHandler
 {
@@ -88,7 +83,7 @@ public class RestUserPreferenceHandler
         awaitFuture(userPreferences.delete(type, name, id));
     }
 
-    void handlePUT(ConfiguredObject<?> target, RequestInfo requestInfo, Object providedObject)
+    public void handlePUT(ConfiguredObject<?> target, RequestInfo requestInfo, Object providedObject)
     {
         UserPreferences userPreferences = target.getUserPreferences();
         if (userPreferences == null)
@@ -146,7 +141,7 @@ public class RestUserPreferenceHandler
         }
     }
 
-    void handlePOST(ConfiguredObject<?> target, RequestInfo requestInfo, Object providedObject)
+    public void handlePOST(ConfiguredObject<?> target, RequestInfo requestInfo, Object providedObject)
     {
         UserPreferences userPreferences = target.getUserPreferences();
         if (userPreferences == null)
@@ -185,7 +180,7 @@ public class RestUserPreferenceHandler
         awaitFuture(userPreferences.updateOrAppend(preferences));
     }
 
-    Object handleGET(UserPreferences userPreferences, RequestInfo requestInfo)
+    public Object handleGET(UserPreferences userPreferences, RequestInfo requestInfo)
     {
         if (userPreferences == null)
         {
@@ -197,11 +192,11 @@ public class RestUserPreferenceHandler
         UUID id = getIdFromQueryParameters(queryParameters);
 
         final ListenableFuture<Set<Preference>> allPreferencesFuture;
-        if (requestInfo.getType() == RequestInfo.RequestType.USER_PREFERENCES)
+        if (requestInfo.getType() == RequestType.USER_PREFERENCES)
         {
             allPreferencesFuture = userPreferences.getPreferences();
         }
-        else if (requestInfo.getType() == RequestInfo.RequestType.VISIBLE_PREFERENCES)
+        else if (requestInfo.getType() == RequestType.VISIBLE_PREFERENCES)
         {
             allPreferencesFuture = userPreferences.getVisiblePreferences();
         }
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerTest.java
new file mode 100644
index 0000000..39a22fa
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/latest/LatestManagementControllerTest.java
@@ -0,0 +1,809 @@
+/*
+ *
+ * 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.management.plugin.controller.latest;
+
+import static org.apache.qpid.server.management.plugin.HttpManagementConfiguration.PREFERENCE_OPERTAION_TIMEOUT_CONTEXT_NAME;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.security.auth.Subject;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementRequest;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.RequestType;
+import org.apache.qpid.server.model.AuthenticationProvider;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.BrokerModel;
+import org.apache.qpid.server.model.BrokerTestHelper;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Queue;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.model.VirtualHostNode;
+import org.apache.qpid.server.model.preferences.GenericPreferenceValueFactory;
+import org.apache.qpid.server.model.preferences.Preference;
+import org.apache.qpid.server.model.preferences.PreferenceImpl;
+import org.apache.qpid.server.security.auth.AuthenticatedPrincipal;
+import org.apache.qpid.server.security.auth.UsernamePrincipal;
+import org.apache.qpid.server.virtualhost.QueueManagingVirtualHost;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class LatestManagementControllerTest extends UnitTestBase
+{
+    private LatestManagementController _controller;
+
+    @Before
+    public void setUp()
+    {
+        final HttpManagementConfiguration<?> httpManagement = mock(HttpManagementConfiguration.class);
+        when(httpManagement.getContextValue(Long.class, PREFERENCE_OPERTAION_TIMEOUT_CONTEXT_NAME)).thenReturn(1000L);
+        _controller = new LatestManagementController(httpManagement);
+    }
+
+    @Test
+    public void getVersion()
+    {
+        assertThat(_controller.getVersion(), is(equalTo(BrokerModel.MODEL_VERSION)));
+    }
+
+    @Test
+    public void getCategories()
+    {
+        assertThat(_controller.getCategories(), is(equalTo(BrokerModel.getInstance()
+                                                                      .getSupportedCategories()
+                                                                      .stream()
+                                                                      .map(Class::getSimpleName)
+                                                                      .collect(Collectors.toSet()))));
+    }
+
+    @Test
+    public void getCategoryMapping()
+    {
+        assertThat(_controller.getCategoryMapping("foo"),
+                   is(equalTo(String.format("/api/v%s/%s/", BrokerModel.MODEL_VERSION, "foo"))));
+    }
+
+    @Test
+    public void getCategory()
+    {
+        final ConfiguredObject<?> object = mock(ConfiguredObject.class);
+        doReturn(Broker.class).when(object).getCategoryClass();
+        assertThat(_controller.getCategory(object), is(equalTo(Broker.class.getSimpleName())));
+    }
+
+    @Test
+    public void getCategoryHierarchyForBrokerRootAndQueueCategory()
+    {
+        final Broker<?> object = BrokerTestHelper.createBrokerMock();
+        final Collection<String> expected = Arrays.asList("VirtualHostNode", "VirtualHost", "Queue");
+        assertThat(_controller.getCategoryHierarchy(object, "Queue"), is(equalTo(expected)));
+    }
+
+    @Test
+    public void getCategoryHierarchyForVirtualHostRootAndExchangeCategory() throws Exception
+    {
+        final QueueManagingVirtualHost<?> object = BrokerTestHelper.createVirtualHost("test", this);
+        final Collection<String> expected = Collections.singletonList("Exchange");
+        assertThat(_controller.getCategoryHierarchy(object, "Exchange"), is(equalTo(expected)));
+    }
+
+
+    @Test
+    public void getCategoryHierarchyForBrokerRootAndUnknownCategory()
+    {
+        final Broker<?> object = BrokerTestHelper.createBrokerMock();
+        final Collection<String> expected = Collections.emptyList();
+        assertThat(_controller.getCategoryHierarchy(object, "Binding"), is(equalTo(expected)));
+    }
+
+    @Test
+    public void getNextVersionManagementController()
+    {
+        assertThat(_controller.getNextVersionManagementController(), is(nullValue()));
+    }
+
+    @Test
+    public void getRequestTypeForGetAndModelObjectWithNotFullPath() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final ManagementRequest request = mock(ManagementRequest.class);
+        when(request.getCategory()).thenReturn("queue");
+        doReturn(virtualHost.getBroker()).when(request).getRoot();
+        when(request.getPath()).thenReturn(Arrays.asList("*", hostName));
+        when(request.getParameters()).thenReturn(Collections.emptyMap());
+        when(request.getMethod()).thenReturn("GET");
+
+        final RequestType type = _controller.getRequestType(request);
+
+        assertThat(type, is(equalTo(RequestType.MODEL_OBJECT)));
+    }
+
+    @Test
+    public void getRequestTypeForGetAndModelObjectWithFullPath() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final ManagementRequest request = mock(ManagementRequest.class);
+        when(request.getCategory()).thenReturn("queue");
+        doReturn(virtualHost.getBroker()).when(request).getRoot();
+        final List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "bar");
+        when(request.getPath()).thenReturn(path);
+        when(request.getParameters()).thenReturn(Collections.emptyMap());
+        when(request.getMethod()).thenReturn("GET");
+
+        final RequestType type = _controller.getRequestType(request);
+
+        assertThat(type, is(equalTo(RequestType.MODEL_OBJECT)));
+    }
+
+    @Test
+    public void getRequestTypeForGetAndUserPreferences() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final ManagementRequest request = mock(ManagementRequest.class);
+        when(request.getCategory()).thenReturn("queue");
+        doReturn(virtualHost.getBroker()).when(request).getRoot();
+        List<String> path = Arrays.asList(virtualHost.getParent().getName(),
+                                          hostName,
+                                          "bar",
+                                          "userpreferences");
+        when(request.getPath()).thenReturn(path);
+        when(request.getParameters()).thenReturn(Collections.emptyMap());
+        when(request.getMethod()).thenReturn("GET");
+
+        final RequestType type = _controller.getRequestType(request);
+
+        assertThat(type, is(equalTo(RequestType.USER_PREFERENCES)));
+    }
+
+    @Test
+    public void getRequestTypeForGetAndVisiblePreferences() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final ManagementRequest request = mock(ManagementRequest.class);
+        when(request.getCategory()).thenReturn("queue");
+        doReturn(virtualHost.getBroker()).when(request).getRoot();
+        List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "bar", "visiblepreferences");
+        when(request.getPath()).thenReturn(path);
+        when(request.getParameters()).thenReturn(Collections.emptyMap());
+        when(request.getMethod()).thenReturn("GET");
+
+        final RequestType type = _controller.getRequestType(request);
+
+        assertThat(type, is(equalTo(RequestType.VISIBLE_PREFERENCES)));
+    }
+
+    @Test
+    public void getForBrokerRootAndQueueSingletonPath() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+        final String nodeName = virtualHost.getParent().getName();
+        final List<String> path = Arrays.asList(nodeName, hostName, "foo");
+
+        final Object object = _controller.get(virtualHost.getBroker(), "queue", path, Collections.emptyMap());
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(Queue.class)));
+
+        final Queue data = (Queue) object;
+        assertThat(data.getName(), is(equalTo("foo")));
+    }
+
+    @Test
+    public void getForBrokerRootAndQueuePathNoQueueName() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+        final String nodeName = virtualHost.getParent().getName();
+        final List<String> path = Arrays.asList(nodeName, hostName);
+
+        final Object object = _controller.get(virtualHost.getBroker(), "queue", path, Collections.emptyMap());
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(Collection.class)));
+
+        final Collection<?> data = (Collection<?>) object;
+        final Iterator iterator = data.iterator();
+        final Object o = iterator.next();
+        final Object o2 = iterator.next();
+        assertThat(o, is(notNullValue()));
+        assertThat(o, is(instanceOf(Queue.class)));
+        assertThat(((Queue) o).getName(), is(equalTo("foo")));
+
+        assertThat(o2, is(notNullValue()));
+        assertThat(o2, is(instanceOf(Queue.class)));
+        assertThat(((Queue) o2).getName(), is(equalTo("bar")));
+    }
+
+    @Test
+    public void getForBrokerRootAndQueuePathWithWildCards() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+        final List<String> path = Arrays.asList("*", hostName);
+
+        final Object object = _controller.get(virtualHost.getBroker(), "queue", path, Collections.emptyMap());
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(Collection.class)));
+
+        final Collection<?> data = (Collection<?>) object;
+        assertThat(data.size(), is(equalTo(2)));
+        final Iterator iterator = data.iterator();
+        final Object o = iterator.next();
+        final Object o2 = iterator.next();
+        assertThat(o, is(notNullValue()));
+        assertThat(o, is(instanceOf(Queue.class)));
+        assertThat(((Queue) o).getName(), is(equalTo("foo")));
+
+        assertThat(o2, is(notNullValue()));
+        assertThat(o2, is(instanceOf(Queue.class)));
+        assertThat(((Queue) o2).getName(), is(equalTo("bar")));
+    }
+
+    @Test
+    public void getForBrokerRootAndQueuePathWithFilter() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar", "bar2");
+        final List<String> path = Arrays.asList("*", hostName);
+
+        final Object object = _controller.get(virtualHost.getBroker(),
+                                              "queue",
+                                              path,
+                                              Collections.singletonMap(Queue.NAME, Arrays.asList("foo", "bar")));
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(Collection.class)));
+
+        final Collection<?> data = (Collection<?>) object;
+        assertThat(data.size(), is(equalTo(2)));
+        final Iterator iterator = data.iterator();
+        final Object o = iterator.next();
+        final Object o2 = iterator.next();
+        assertThat(o, is(notNullValue()));
+        assertThat(o, is(instanceOf(Queue.class)));
+        assertThat(((Queue) o).getName(), is(equalTo("foo")));
+
+        assertThat(o2, is(notNullValue()));
+        assertThat(o2, is(instanceOf(Queue.class)));
+        assertThat(((Queue) o2).getName(), is(equalTo("bar")));
+    }
+
+    @Test
+    public void createOrUpdateUsingPutAndFullPath() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName);
+        final List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "bar");
+
+        final Object object = _controller.createOrUpdate(virtualHost.getBroker(),
+                                                         "queue",
+                                                         path,
+                                                         new HashMap<>(Collections.singletonMap(Queue.NAME, "bar")),
+                                                         false);
+
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(Queue.class)));
+        assertThat(((Queue) object).getName(), is(equalTo("bar")));
+    }
+
+    @Test
+    public void createOrUpdateUsingPostAndFullPathForNonExisting() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName);
+        final List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "bar");
+
+        try
+        {
+            _controller.createOrUpdate(virtualHost.getBroker(),
+                                       "queue",
+                                       path,
+                                       new HashMap<>(Collections.singletonMap(Queue.NAME, "bar")),
+                                       true);
+            fail("Post update should fail for non existing");
+        }
+        catch (ManagementException e)
+        {
+            assertThat(e.getStatusCode(), is(equalTo(404)));
+        }
+    }
+
+    @Test
+    public void createOrUpdateUsingPostAndFullPathForExisting() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "bar");
+        final List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "bar");
+
+        final Object object = _controller.createOrUpdate(virtualHost.getBroker(),
+                                                         "queue",
+                                                         path,
+                                                         new HashMap<>(Collections.singletonMap(Queue.NAME, "bar")),
+                                                         true);
+
+        assertThat(object, is(nullValue()));
+    }
+
+    @Test
+    public void createOrUpdateUsingPostAndParentPath() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName);
+        final List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName);
+
+        final Object object = _controller.createOrUpdate(virtualHost.getBroker(),
+                                                         "queue",
+                                                         path,
+                                                         new HashMap<>(Collections.singletonMap(Queue.NAME, "bar")),
+                                                         true);
+
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(Queue.class)));
+        assertThat(((Queue) object).getName(), is(equalTo("bar")));
+    }
+
+    @Test
+    public void deleteUsingFullPath() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "bar");
+
+        int count = _controller.delete(virtualHost.getBroker(), "queue", path, Collections.emptyMap());
+
+        assertThat(count, is(equalTo(1)));
+    }
+
+    @Test
+    public void deleteUsingFilter() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName);
+
+        int count = _controller.delete(virtualHost.getBroker(),
+                                       "queue",
+                                       path,
+                                       Collections.singletonMap(Queue.NAME, Arrays.asList("foo", "bar", "bar2")));
+
+        assertThat(count, is(equalTo(2)));
+    }
+
+    @Test
+    public void deleteUsingWildcard() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "*");
+
+        int count = _controller.delete(virtualHost.getBroker(), "queue", path, Collections.emptyMap());
+
+        assertThat(count, is(equalTo(2)));
+    }
+
+    @Test
+    public void invoke() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName);
+
+        Map<String, Object> message = new HashMap<>();
+        message.put("address", "foo");
+        message.put("persistent", "false");
+        message.put("content", "Test Content");
+        message.put("mimeType", "text/plain");
+        ManagementResponse response = _controller.invoke(virtualHost.getBroker(),
+                                                         "virtualhost",
+                                                         path,
+                                                         "publishMessage",
+                                                         Collections.singletonMap("message", message),
+                                                         true,
+                                                         true);
+
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        Object body = response.getBody();
+        assertThat(body, is(instanceOf(Number.class)));
+        assertThat(((Number) body).intValue(), is(equalTo(1)));
+    }
+
+    @Test
+    public void getPreferences() throws Exception
+    {
+        final String hostName = "default";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName);
+        final String preferencesType = "X-type-preference";
+        final Map<String, Object> preferenceValue = Collections.singletonMap("foo", "bar");
+        final Subject testSubject = createTestSubject();
+        final String prefernceName = "test";
+        createPreferences(testSubject, virtualHost, preferencesType, prefernceName, preferenceValue);
+
+        List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "userpreferences");
+        final Object preferences = Subject.doAs(testSubject, (PrivilegedAction<Object>) () ->
+                _controller.getPreferences(virtualHost.getBroker(), "virtualhost", path, Collections.emptyMap()));
+
+        assertPreference(preferencesType, prefernceName, preferenceValue, preferences);
+    }
+
+
+    @Test
+    public void setPreferences() throws Exception
+    {
+        final String hostName = "default";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName);
+        final String preferencesType = "X-type";
+        final Map<String, Object> preferenceValue = Collections.singletonMap("foo", "bar");
+        final Subject testSubject = createTestSubject();
+        final String preferenceName = "pref";
+        final UUID id = createPreferences(testSubject, virtualHost, preferencesType, preferenceName, preferenceValue);
+        final List<String> path = Arrays.asList(virtualHost.getParent().getName(), hostName, "userpreferences");
+        final Map<String, Object> newValue = Collections.singletonMap("foo", "bar2");
+        final Map<String, Object> data = new HashMap<>();
+        data.put("id", id.toString());
+        data.put("name", preferenceName);
+        data.put("value", newValue);
+        final Map<String, List<Object>> modifiedPreferences = Collections.singletonMap(preferencesType,
+                                                                                       Collections.singletonList(data));
+        Subject.doAs(testSubject, (PrivilegedAction<Void>) () -> {
+            _controller.setPreferences(virtualHost.getBroker(),
+                                       "virtualhost",
+                                       path,
+                                       modifiedPreferences,
+                                       Collections.emptyMap(),
+                                       true);
+            return null;
+        });
+        final Object preferences = Subject.doAs(testSubject, (PrivilegedAction<Object>) () ->
+                _controller.getPreferences(virtualHost.getBroker(), "virtualhost", path, Collections.emptyMap()));
+
+        assertPreference(preferencesType, preferenceName, newValue, preferences);
+    }
+
+    @Test
+    public void deletePreferences() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName);
+        final String preferencesType = "X-type";
+        final Map<String, Object> preferenceValue = Collections.singletonMap("foo", "bar");
+        final Subject testSubject = createTestSubject();
+        final String preferenceName = "pref";
+        createPreferences(testSubject, virtualHost, preferencesType, preferenceName, preferenceValue);
+
+        final List<String> path = Arrays.asList(virtualHost.getParent().getName(),
+                                                hostName,
+                                                "userpreferences",
+                                                preferencesType,
+                                                preferenceName);
+
+        Subject.doAs(testSubject, (PrivilegedAction<Void>) () -> {
+            _controller.deletePreferences(virtualHost.getBroker(),
+                                          "virtualhost",
+                                          path,
+                                          Collections.emptyMap());
+            return null;
+        });
+
+        final List<String> path2 = Arrays.asList(virtualHost.getParent().getName(), hostName, "userpreferences");
+
+        final Object preferences = Subject.doAs(testSubject, (PrivilegedAction<Object>) () ->
+                _controller.getPreferences(virtualHost.getBroker(), "virtualhost", path2, Collections.emptyMap()));
+        assertThat(preferences, is(notNullValue()));
+        assertThat(preferences, is(instanceOf(Map.class)));
+
+        final Map<?, ?> map = (Map<?, ?>) preferences;
+        assertThat(map.size(), is(equalTo(0)));
+    }
+
+    @Test
+    public void formatConfiguredObjectForSingletonResponse() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final Object formatted = _controller.formatConfiguredObject(virtualHost,
+                                                                    Collections.singletonMap("depth",
+                                                                                             Collections.singletonList(
+                                                                                                     "1")),
+                                                                    true);
+        assertThat(formatted, is(notNullValue()));
+        assertThat(formatted, is(instanceOf(Map.class)));
+
+        final Map<?, ?> data = (Map<?, ?>) formatted;
+        assertThat(data.get(VirtualHost.NAME), is(equalTo(hostName)));
+        final Object queues = data.get("queues");
+        assertThat(queues, is(notNullValue()));
+        assertThat(queues, is(instanceOf(Collection.class)));
+
+        final Collection<?> queueCollection = (Collection<?>) queues;
+
+        assertThat(queueCollection.size(), is(equalTo(2)));
+        final Iterator<?> iterator = queueCollection.iterator();
+        final Object queue1 = iterator.next();
+        final Object queue2 = iterator.next();
+
+        assertThat(queue1, is(instanceOf(Map.class)));
+        assertThat(queue2, is(instanceOf(Map.class)));
+
+        final Map<?, ?> queueMap1 = (Map<?, ?>) queue1;
+        final Map<?, ?> queueMap2 = (Map<?, ?>) queue2;
+
+        assertThat(queueMap1.get(Queue.NAME), is(equalTo("bar")));
+        assertThat(queueMap2.get(Queue.NAME), is(equalTo("foo")));
+    }
+
+    @Test
+    public void formatConfiguredObjectForCollectionResponse() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final Object formatted = _controller.formatConfiguredObject(Collections.singletonList(virtualHost),
+                                                                    Collections.singletonMap("depth",
+                                                                                             Collections.singletonList(
+                                                                                                     "1")),
+                                                                    true);
+        assertThat(formatted, is(notNullValue()));
+        assertThat(formatted, is(instanceOf(Collection.class)));
+
+        final Collection<?> formattedCollection = (Collection<?>) formatted;
+        assertThat(formattedCollection.size(), is(equalTo(1)));
+
+        Object item = formattedCollection.iterator().next();
+        assertThat(item, is(instanceOf(Map.class)));
+
+        final Map<?, ?> data = (Map<?, ?>) item;
+        assertThat(data.get(VirtualHost.NAME), is(equalTo(hostName)));
+        final Object queues = data.get("queues");
+        assertThat(queues, is(notNullValue()));
+        assertThat(queues, is(instanceOf(Collection.class)));
+
+        final Collection<?> queueCollection = (Collection<?>) queues;
+
+        assertThat(queueCollection.size(), is(equalTo(2)));
+        final Iterator<?> iterator = queueCollection.iterator();
+        final Object queue1 = iterator.next();
+        final Object queue2 = iterator.next();
+
+        assertThat(queue1, is(instanceOf(Map.class)));
+        assertThat(queue2, is(instanceOf(Map.class)));
+
+        final Map<?, ?> queueMap1 = (Map<?, ?>) queue1;
+        final Map<?, ?> queueMap2 = (Map<?, ?>) queue2;
+
+        assertThat(queueMap1.get(Queue.NAME), is(equalTo("bar")));
+        assertThat(queueMap2.get(Queue.NAME), is(equalTo("foo")));
+    }
+
+    @Test
+    public void handleGetForBrokerRootAndQueueSingletonPath() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final String nodeName = virtualHost.getParent().getName();
+        final ManagementRequest request = mock(ManagementRequest.class);
+        when(request.getCategory()).thenReturn("queue");
+        doReturn(virtualHost.getBroker()).when(request).getRoot();
+        when(request.getPath()).thenReturn(Arrays.asList(nodeName, hostName, "foo"));
+        when(request.getMethod()).thenReturn("GET");
+
+        final ManagementResponse response = _controller.handleGet(request);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        assertThat(response.getBody(), is(notNullValue()));
+        assertThat(response.getBody(), is(instanceOf(Queue.class)));
+
+        final Queue data = (Queue) response.getBody();
+        assertThat(data.getName(), is(equalTo("foo")));
+    }
+
+
+    @Test
+    public void handleGetForBrokerRootAndQueuePathWithoutQueueName() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final String nodeName = virtualHost.getParent().getName();
+        final ManagementRequest request = mock(ManagementRequest.class);
+        when(request.getCategory()).thenReturn("queue");
+        doReturn(virtualHost.getBroker()).when(request).getRoot();
+        when(request.getPath()).thenReturn(Arrays.asList(nodeName, hostName));
+        when(request.getParameters()).thenReturn(Collections.emptyMap());
+        when(request.getMethod()).thenReturn("GET");
+
+        final ManagementResponse response = _controller.handleGet(request);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        assertThat(response.getBody(), is(notNullValue()));
+        assertThat(response.getBody(), is(instanceOf(Collection.class)));
+
+        final Collection data = (Collection) response.getBody();
+        assertThat(data.size(), is(equalTo(2)));
+
+        final Iterator iterator = data.iterator();
+        final Object object = iterator.next();
+        final Object object2 = iterator.next();
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(Queue.class)));
+        assertThat(((Queue) object).getName(), is(equalTo("foo")));
+
+        assertThat(object2, is(notNullValue()));
+        assertThat(object2, is(instanceOf(Queue.class)));
+        assertThat(((Queue) object2).getName(), is(equalTo("bar")));
+    }
+
+    @Test
+    public void handleGetForBrokerRootAndQueuePathWithFilter() throws Exception
+    {
+        final String hostName = "test";
+        final QueueManagingVirtualHost<?> virtualHost = createVirtualHostWithQueue(hostName, "foo", "bar");
+
+        final String nodeName = virtualHost.getParent().getName();
+        final ManagementRequest request = mock(ManagementRequest.class);
+        when(request.getCategory()).thenReturn("queue");
+        doReturn(virtualHost.getBroker()).when(request).getRoot();
+        when(request.getPath()).thenReturn(Arrays.asList(nodeName, hostName));
+        when(request.getParameters()).thenReturn(Collections.singletonMap("name", Collections.singletonList("bar")));
+        when(request.getMethod()).thenReturn("GET");
+
+        ManagementResponse response = _controller.handleGet(request);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        assertThat(response.getBody(), is(notNullValue()));
+        assertThat(response.getBody(), is(instanceOf(Collection.class)));
+
+        Collection data = (Collection) response.getBody();
+        assertThat(data.size(), is(equalTo(1)));
+
+        Object object = data.iterator().next();
+        assertThat(object, is(notNullValue()));
+        assertThat(object, is(instanceOf(Queue.class)));
+        assertThat(((Queue) object).getName(), is(equalTo("bar")));
+    }
+
+    private QueueManagingVirtualHost<?> createVirtualHostWithQueue(final String hostName, String... queueName)
+            throws Exception
+    {
+        final QueueManagingVirtualHost<?> virtualHost = BrokerTestHelper.createVirtualHost(hostName, this);
+        final Broker root = virtualHost.getBroker();
+        final ConfiguredObject<?> virtualHostNode = virtualHost.getParent();
+        when(root.getChildren(VirtualHostNode.class)).thenReturn(Collections.singletonList(virtualHostNode));
+        when(virtualHostNode.getChildren(VirtualHost.class)).thenReturn(Collections.singletonList(virtualHost));
+        when(virtualHostNode.getChildByName(VirtualHost.class, hostName)).thenReturn(virtualHost);
+        Stream.of(queueName)
+              .forEach(n -> virtualHost.createChild(Queue.class, Collections.singletonMap(Queue.NAME, n)));
+        return virtualHost;
+    }
+
+
+    private UUID createPreferences(final Subject testSubject,
+                                   final QueueManagingVirtualHost<?> virtualHost,
+                                   final String preferenceType,
+                                   final String preferenceName,
+                                   final Map<String, Object> preferenceValue)
+            throws Exception
+    {
+        UUID uuid = UUID.randomUUID();
+        final Preference preference = new PreferenceImpl(virtualHost,
+                                                         uuid,
+                                                         preferenceName,
+                                                         preferenceType,
+                                                         "Some preference",
+                                                         null,
+                                                         new Date(),
+                                                         new Date(),
+                                                         null,
+                                                         new GenericPreferenceValueFactory().createInstance(
+                                                                 preferenceValue));
+        final List<Preference> preferenceList = Collections.singletonList(preference);
+        final Future<Void> result = Subject.doAs(testSubject,
+                                                 (PrivilegedAction<Future<Void>>) () -> virtualHost.getUserPreferences()
+                                                                                                   .updateOrAppend(
+                                                                                                           preferenceList));
+
+        result.get(2000L, TimeUnit.MILLISECONDS);
+        return uuid;
+    }
+
+    private Subject createTestSubject()
+    {
+        final AuthenticationProvider<?> provider = mock(AuthenticationProvider.class);
+        when(provider.getType()).thenReturn("type");
+        when(provider.getName()).thenReturn("name");
+
+        return new Subject(false,
+                           Collections.singleton(new AuthenticatedPrincipal(new UsernamePrincipal("user", provider))),
+                           Collections.emptySet(),
+                           Collections.emptySet());
+    }
+
+    private void assertPreference(final String expectedType,
+                                  final String expectedName,
+                                  final Map<String, Object> expectedValue,
+                                  final Object preferences)
+    {
+        assertThat(preferences, is(notNullValue()));
+        assertThat(preferences, is(instanceOf(Map.class)));
+
+        final Map<?, ?> data = (Map<?, ?>) preferences;
+
+        final Object pt = data.get(expectedType);
+        assertThat(pt, is(notNullValue()));
+        assertThat(pt, is(instanceOf(Collection.class)));
+
+        final Collection<?> items = (Collection<?>) pt;
+        assertThat(items.size(), is(equalTo(1)));
+
+        final Object item = items.iterator().next();
+        assertThat(item, is(notNullValue()));
+        assertThat(item, is(instanceOf(Map.class)));
+
+        final Map<?, ?> map = (Map<?, ?>) item;
+
+        final Object value = map.get("value");
+        assertThat(value, is(notNullValue()));
+        assertThat(value, is(equalTo(expectedValue)));
+
+        final Object name = map.get("name");
+        assertThat(name, is(notNullValue()));
+        assertThat(name, is(equalTo(expectedName)));
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java
index e18ff34..5ffafbb 100644
--- a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RequestInfoParserTest.java
@@ -36,6 +36,7 @@ import javax.servlet.http.HttpServletRequest;
 
 import org.junit.Test;
 
+import org.apache.qpid.server.management.plugin.RequestType;
 import org.apache.qpid.server.model.Queue;
 import org.apache.qpid.server.model.VirtualHost;
 import org.apache.qpid.server.model.VirtualHostNode;
@@ -54,7 +55,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Collections.emptyList(), info.getModelParts());
     }
 
@@ -71,7 +72,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
         assertTrue("Expected exact object request", info.isSingletonRequest());
     }
@@ -85,7 +86,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName), info.getModelParts());
         assertFalse("Expected exact object request", info.isSingletonRequest());
     }
@@ -126,7 +127,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName), info.getModelParts());
     }
 
@@ -144,7 +145,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
     }
 
@@ -203,7 +204,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
     }
 
@@ -218,7 +219,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
     }
 
@@ -271,7 +272,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
     }
 
@@ -287,7 +288,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName, vhName), info.getModelParts());
     }
 
@@ -320,7 +321,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.MODEL_OBJECT, info.getType());
+        assertEquals("Unexpected request type", RequestType.MODEL_OBJECT, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName), info.getModelParts());
     }
 
@@ -359,7 +360,7 @@ public class RequestInfoParserTest extends UnitTestBase
 
         RequestInfo info = parser.parse(_request);
 
-        assertEquals("Unexpected request type", RequestInfo.RequestType.OPERATION, info.getType());
+        assertEquals("Unexpected request type", RequestType.OPERATION, info.getType());
         assertEquals("Unexpected model parts", Arrays.asList(vhnName), info.getModelParts());
         assertEquals("Unexpected operation name", operationName, info.getOperationName());
         assertTrue("Expected exact object request", info.isSingletonRequest());


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[qpid-broker-j] 03/03: QPID-6948: [Broker-J] Add compatibility layer for REST API v6.1

Posted by or...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

orudyy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git

commit f4807cbd0297203c322deb54ce02b2c54e19cf44
Author: Alex Rudyy <or...@apache.org>
AuthorDate: Tue Dec 18 17:20:22 2018 +0000

    QPID-6948: [Broker-J] Add compatibility layer for REST API v6.1
---
 .../v6_1/LegacyManagementController.java           | 194 +++++++
 .../v6_1/LegacyManagementControllerFactory.java    |  61 ++
 .../v6_1/category/BindingController.java           | 611 +++++++++++++++++++++
 .../controller/v6_1/category/BrokerController.java | 212 +++++++
 .../v6_1/category/ConsumerController.java          | 257 +++++++++
 .../v6_1/category/DestinationController.java       | 207 +++++++
 .../v6_1/category/ExchangeController.java          |  92 ++++
 .../v6_1/category/LegacyCategoryController.java    |  62 +++
 .../category/LegacyCategoryControllerFactory.java  | 268 +++++++++
 .../controller/v6_1/category/PortController.java   | 140 +++++
 .../controller/v6_1/category/QueueController.java  | 273 +++++++++
 .../v6_1/category/SessionController.java           | 111 ++++
 .../v6_1/category/VirtualHostController.java       | 130 +++++
 .../v6_1/LegacyManagementControllerTest.java       | 138 +++++
 .../v6_1/category/BindingControllerTest.java       | 312 +++++++++++
 .../v6_1/category/BrokerControllerTest.java        | 104 ++++
 .../v6_1/category/ConsumerControllerTest.java      | 317 +++++++++++
 .../v6_1/category/DestinationControllerTest.java   | 135 +++++
 .../v6_1/category/ExchangeControllerTest.java      | 113 ++++
 .../LegacyCategoryControllerFactoryTest.java       |  63 +++
 .../category/LegacyCategoryControllerTest.java     |  80 +++
 .../v6_1/category/PortControllerTest.java          | 113 ++++
 .../v6_1/category/QueueControllerTest.java         | 161 ++++++
 .../v6_1/category/SessionControllerTest.java       |  95 ++++
 .../v6_1/category/VirtualHostControllerTest.java   | 104 ++++
 25 files changed, 4353 insertions(+)

diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementController.java
new file mode 100644
index 0000000..773db9d
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementController.java
@@ -0,0 +1,194 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1;
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createUnprocessableManagementException;
+import static org.apache.qpid.server.management.plugin.controller.ConverterHelper.getIntParameterFromRequest;
+import static org.apache.qpid.server.management.plugin.controller.ConverterHelper.getParameter;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.controller.AbstractLegacyConfiguredObjectController;
+import org.apache.qpid.server.management.plugin.controller.ConverterHelper;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public class LegacyManagementController extends AbstractLegacyConfiguredObjectController
+{
+    private static final String DEPTH_PARAM = "depth";
+    private static final String OVERSIZE_PARAM = "oversize";
+    private static final String ACTUALS_PARAM = "actuals";
+    private static final String INCLUDE_SYS_CONTEXT_PARAM = "includeSysContext";
+    private static final String INHERITED_ACTUALS_PARAM = "inheritedActuals";
+    private static final String EXCLUDE_INHERITED_CONTEXT_PARAM = "excludeInheritedContext";
+
+
+    private static final int DEFAULT_DEPTH = 1;
+    private static final int DEFAULT_OVERSIZE = 120;
+
+    LegacyManagementController(final ManagementController nextVersionManagementController)
+    {
+        super(LegacyManagementControllerFactory.MODEL_VERSION, nextVersionManagementController);
+    }
+
+    @Override
+    public Object get(final ConfiguredObject<?> root,
+                      final String category,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        Object result = super.get(root, category, path, convertQueryParameters(parameters));
+        if (result instanceof LegacyConfiguredObject)
+        {
+            return Collections.singletonList(result);
+        }
+        return result;
+    }
+
+
+    @Override
+    public Map<String, List<String>> convertQueryParameters(final Map<String, List<String>> requestParameters)
+    {
+        Map<String, List<String>> params = requestParameters
+                .entrySet()
+                .stream()
+                .filter(e -> !INCLUDE_SYS_CONTEXT_PARAM.equals(e.getKey())
+                             && !INHERITED_ACTUALS_PARAM.equals(e.getKey()))
+                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+        Map<String, List<String>> parameters = new HashMap<>(params);
+        boolean excludeInheritedContext = isInheritedContextExcluded(params);
+        parameters.put(EXCLUDE_INHERITED_CONTEXT_PARAM,
+                       Collections.singletonList(String.valueOf(excludeInheritedContext)));
+        if (!parameters.containsKey(DEPTH_PARAM))
+        {
+            parameters.put(DEPTH_PARAM, Collections.singletonList("1"));
+        }
+        return parameters;
+    }
+
+    @Override
+    public Object formatConfiguredObject(final Object content,
+                                         final Map<String, List<String>> parameters,
+                                         final boolean isSecureOrAllowedOnInsecureChannel)
+    {
+        final int depth = getIntParameterFromRequest(parameters, DEPTH_PARAM, DEFAULT_DEPTH);
+        final int oversizeThreshold = getIntParameterFromRequest(parameters, OVERSIZE_PARAM, DEFAULT_OVERSIZE);
+        final boolean actuals = Boolean.parseBoolean(getParameter(ACTUALS_PARAM, parameters));
+        final boolean excludeInheritedContext = isInheritedContextExcluded(parameters);
+
+        if (content instanceof LegacyConfiguredObject)
+        {
+            LegacyConfiguredObject legacyConfiguredObjectObject = (LegacyConfiguredObject) content;
+            return convertManageableToMap(
+                    legacyConfiguredObjectObject,
+                    depth,
+                    actuals,
+                    oversizeThreshold,
+                    excludeInheritedContext);
+        }
+        else if (content instanceof Collection)
+        {
+            return ((Collection<?>) content).stream()
+                                            .filter(o -> o instanceof LegacyConfiguredObject)
+                                            .map(LegacyConfiguredObject.class::cast)
+                                            .map(o -> convertManageableToMap(
+                                                    o,
+                                                    depth,
+                                                    actuals,
+                                                    oversizeThreshold,
+                                                    excludeInheritedContext))
+                                            .collect(Collectors.toSet());
+        }
+        return content;
+    }
+
+
+    private Map<String, Object> convertManageableToMap(final LegacyConfiguredObject legacyConfiguredObjectObject,
+                                                       final int depth,
+                                                       final boolean actuals,
+                                                       final int oversizeThreshold,
+                                                       final boolean excludeInheritedContext)
+    {
+        return convertObject(legacyConfiguredObjectObject,
+                             depth,
+                             actuals,
+                             oversizeThreshold,
+                             true,
+                             excludeInheritedContext);
+    }
+
+
+    private boolean isInheritedContextExcluded(final Map<String, List<String>> params)
+    {
+        boolean excludeInheritedContext;
+        boolean actuals = Boolean.parseBoolean(ConverterHelper.getParameter(ACTUALS_PARAM, params));
+        String includeSystemContextParameter = ConverterHelper.getParameter(INCLUDE_SYS_CONTEXT_PARAM, params);
+        String inheritedActualsParameter = ConverterHelper.getParameter(INHERITED_ACTUALS_PARAM, params);
+        String excludeInheritedContextParameter =
+                ConverterHelper.getParameter(EXCLUDE_INHERITED_CONTEXT_PARAM, params);
+        if (excludeInheritedContextParameter == null)
+        {
+            if (inheritedActualsParameter == null && includeSystemContextParameter == null)
+            {
+                excludeInheritedContext = actuals;
+            }
+            else if (inheritedActualsParameter != null && includeSystemContextParameter != null)
+            {
+                if (actuals)
+                {
+                    excludeInheritedContext = !Boolean.parseBoolean(inheritedActualsParameter);
+                }
+                else
+                {
+                    excludeInheritedContext = !Boolean.parseBoolean(includeSystemContextParameter);
+                }
+            }
+            else if (inheritedActualsParameter != null)
+            {
+                excludeInheritedContext = actuals && !Boolean.parseBoolean(inheritedActualsParameter);
+            }
+            else
+            {
+                excludeInheritedContext = actuals || !Boolean.parseBoolean(includeSystemContextParameter);
+            }
+        }
+        else
+        {
+            if (inheritedActualsParameter != null || includeSystemContextParameter != null)
+            {
+                throw createUnprocessableManagementException(String.format(
+                        "Parameter '%s' cannot be specified together with '%s' or '%s'",
+                        EXCLUDE_INHERITED_CONTEXT_PARAM,
+                        INHERITED_ACTUALS_PARAM,
+                        INCLUDE_SYS_CONTEXT_PARAM));
+            }
+            excludeInheritedContext = Boolean.parseBoolean(excludeInheritedContextParameter);
+        }
+        return excludeInheritedContext;
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementControllerFactory.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementControllerFactory.java
new file mode 100644
index 0000000..d625b48
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementControllerFactory.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1;
+
+import org.apache.qpid.server.management.plugin.HttpManagementConfiguration;
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementControllerFactory;
+import org.apache.qpid.server.management.plugin.controller.CategoryControllerFactory;
+import org.apache.qpid.server.management.plugin.controller.TypeControllerFactory;
+import org.apache.qpid.server.plugin.PluggableService;
+
+@PluggableService
+public class LegacyManagementControllerFactory implements ManagementControllerFactory
+{
+    public static final String MODEL_VERSION = "6.1";
+
+    @Override
+    public String getType()
+    {
+        return "org.apache.qpid.server.management.plugin.model.v6_1";
+    }
+
+    @Override
+    public String getVersion()
+    {
+        return MODEL_VERSION;
+    }
+
+    @Override
+    public String getPreviousVersion()
+    {
+        return "6.0";
+    }
+
+    @Override
+    public ManagementController createManagementController(final HttpManagementConfiguration<?> httpManagement,
+                                                           final ManagementController nextVersionManagementController)
+    {
+        final LegacyManagementController controller = new LegacyManagementController(nextVersionManagementController);
+        controller.initialize();
+        return controller;
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BindingController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BindingController.java
new file mode 100644
index 0000000..a117d96
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BindingController.java
@@ -0,0 +1,611 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createBadRequestManagementException;
+import static org.apache.qpid.server.management.plugin.ManagementException.createInternalServerErrorManagementException;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+import org.apache.qpid.server.management.plugin.controller.ControllerManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.State;
+
+public class BindingController implements CategoryController
+{
+    public static final String TYPE = "Binding";
+    private final LegacyManagementController _managementController;
+    private final ManagementController _nextVersionManagementController;
+
+    BindingController(final LegacyManagementController managementController)
+    {
+        _managementController = managementController;
+        _nextVersionManagementController = _managementController.getNextVersionManagementController();
+    }
+
+    @Override
+    public String getCategory()
+    {
+        return TYPE;
+    }
+
+    @Override
+    public String getNextVersionCategory()
+    {
+        return null;
+    }
+
+    @Override
+    public String getDefaultType()
+    {
+        return null;
+    }
+
+    @Override
+    public String[] getParentCategories()
+    {
+        return new String[]{ExchangeController.TYPE, QueueController.TYPE};
+    }
+
+    @Override
+    public LegacyManagementController getManagementController()
+    {
+        return _managementController;
+    }
+
+    @Override
+    public Object get(final ConfiguredObject<?> root,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        final Collection<String> hierarchy = _managementController.getCategoryHierarchy(root, getCategory());
+
+        final String bindingName = path.size() == hierarchy.size() ? path.get(hierarchy.size() - 1) : null;
+        final String queueName = path.size() >= hierarchy.size() - 1 ? path.get(hierarchy.size() - 2) : null;
+
+        final List<String> exchangePath =
+                path.size() >= hierarchy.size() - 2 ? path.subList(0, hierarchy.size() - 2) : path;
+
+        return getExchangeBindings(root, exchangePath, queueName, bindingName);
+
+    }
+
+
+    @Override
+    public LegacyConfiguredObject createOrUpdate(final ConfiguredObject<?> root,
+                                                 final List<String> path,
+                                                 final Map<String, Object> attributes,
+                                                 final boolean isPost) throws ManagementException
+    {
+        if (path.contains("*"))
+        {
+            throw createBadRequestManagementException("Wildcards in path are not supported for post and put requests");
+        }
+
+        final Collection<String> hierarchy = _managementController.getCategoryHierarchy(root, getCategory());
+        if (path.size() < hierarchy.size() - 2)
+        {
+            throw createBadRequestManagementException(String.format("Cannot create binding for path %s",
+                                                                    String.join("/" + path)));
+        }
+
+        String queueName = null;
+
+        if (path.size() > hierarchy.size() - 2)
+        {
+            queueName = path.get(hierarchy.size() - 2);
+        }
+        if (queueName == null)
+        {
+            queueName = (String) attributes.get("queue");
+        }
+        if (queueName == null)
+        {
+            throw createBadRequestManagementException(
+                    "Queue is required for binding creation. Please specify queue either in path or in binding attributes");
+        }
+
+        final List<String> exchangePath = path.subList(0, hierarchy.size() - 3);
+        final LegacyConfiguredObject
+                nextVersionExchange = getNextVersionObject(root, exchangePath, ExchangeController.TYPE);
+        final List<String> queuePath = new ArrayList<>(path.subList(0, hierarchy.size() - 4));
+        queuePath.add(queueName);
+        final LegacyConfiguredObject nextVersionQueue =
+                getNextVersionObject(root, queuePath, QueueController.TYPE);
+
+        String bindingKey = (String) attributes.get(GenericLegacyConfiguredObject.NAME);
+        if (bindingKey == null)
+        {
+            bindingKey = path.size() == hierarchy.size() ? path.get(hierarchy.size() - 1) : null;
+        }
+        if (bindingKey == null)
+        {
+            bindingKey = "";
+        }
+
+        final Map<String, Object> parameters = new LinkedHashMap<>();
+        parameters.put("bindingKey", bindingKey);
+        parameters.put("destination", queueName);
+
+        Map<String, Object> arguments = null;
+        if (attributes.containsKey("arguments"))
+        {
+            Object args = attributes.get("arguments");
+            if (args instanceof Map)
+            {
+                @SuppressWarnings("unchecked")
+                Map<String, Object> argumentsMap = (Map<String, Object>) args;
+                arguments = new HashMap<>(argumentsMap);
+                if (!arguments.isEmpty())
+                {
+                    parameters.put("arguments", arguments);
+                }
+            }
+            else
+            {
+                throw createBadRequestManagementException(String.format("Unexpected attributes specified : %s", args));
+            }
+        }
+        parameters.put("replaceExistingArguments", !isPost);
+
+        ManagementResponse response = nextVersionExchange.invoke("bind", parameters, true);
+        final boolean newBindings = Boolean.TRUE.equals(response.getBody());
+        if (!newBindings)
+        {
+            return null;
+        }
+
+        return new LegacyBinding(_managementController,
+                                 nextVersionExchange,
+                                 nextVersionQueue,
+                                 bindingKey,
+                                 arguments);
+    }
+
+    @Override
+    public int delete(final ConfiguredObject<?> root,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        if (path.contains("*"))
+        {
+            throw createBadRequestManagementException("Wildcards in path are not supported for delete requests");
+        }
+
+        final Collection<String> hierarchy = _managementController.getCategoryHierarchy(root, getCategory());
+        if (path.size() < hierarchy.size() - 2)
+        {
+            throw createBadRequestManagementException(String.format("Cannot delete binding for path %s",
+                                                                    String.join("/", path)));
+        }
+
+        final String bindingName = path.size() == hierarchy.size() ? path.get(hierarchy.size() - 1) : null;
+        final String queueName = path.get(hierarchy.size() - 2);
+        final List<String> ids = parameters.get(GenericLegacyConfiguredObject.ID);
+        final List<String> exchangePath = path.subList(0, hierarchy.size() - 3);
+
+        final LegacyConfiguredObject exchange = getNextVersionObject(root, exchangePath, ExchangeController.TYPE);
+
+        final AtomicInteger counter = new AtomicInteger();
+
+        if (ids != null && !ids.isEmpty())
+        {
+            @SuppressWarnings("unchecked")
+            Collection<Binding> bindings = (Collection<Binding>) exchange.getAttribute("bindings");
+            if (bindings != null)
+            {
+                bindings.stream()
+                        .filter(b -> ids.contains(String.valueOf(generateBindingId(exchange,
+                                                                                   b.getDestination(),
+                                                                                   b.getBindingKey()))))
+                        .forEach(b -> {
+
+                            Map<String, Object> params = new LinkedHashMap<>();
+                            params.put("bindingKey", b.getBindingKey());
+                            params.put("destination", b.getDestination());
+                            ManagementResponse r = exchange.invoke("unbind", params, true);
+                            if (Boolean.TRUE.equals(r.getBody()))
+                            {
+                                counter.incrementAndGet();
+                            }
+                        });
+            }
+        }
+        else if (bindingName != null)
+        {
+            Map<String, Object> params = new LinkedHashMap<>();
+            params.put("bindingKey", bindingName);
+            params.put("destination", queueName);
+            ManagementResponse response = exchange.invoke("unbind", params, true);
+            if (Boolean.TRUE.equals(response.getBody()))
+            {
+                counter.incrementAndGet();
+            }
+        }
+        else
+        {
+            throw createBadRequestManagementException("Only deletion by binding full path or ids is supported");
+        }
+
+        return counter.get();
+    }
+
+    private LegacyConfiguredObject getNextVersionObject(final ConfiguredObject<?> root,
+                                                        final List<String> path,
+                                                        final String type)
+    {
+        return (LegacyConfiguredObject) _nextVersionManagementController.get(root,
+                                                                             type.toLowerCase(),
+                                                                             path,
+                                                                             Collections.emptyMap());
+    }
+
+    @Override
+    public ManagementResponse invoke(ConfiguredObject<?> root,
+                                     List<String> path,
+                                     String operation,
+                                     Map<String, Object> parameters,
+                                     boolean isPost, final boolean isSecure) throws ManagementException
+    {
+        Object result = get(root, path, Collections.emptyMap());
+        if (result instanceof Collection && ((Collection)result).size() == 1)
+        {
+            LegacyConfiguredObject  object = (LegacyConfiguredObject) ((Collection<?>)result).iterator().next();
+            return object.invoke(operation, parameters, isSecure);
+        }
+        throw createBadRequestManagementException(String.format("Operation '%s' cannot be invoked for Binding path '%s'",
+                                                                operation,
+                                                                String.join("/", path)));
+    }
+
+
+    @Override
+    public Object getPreferences(ConfiguredObject<?> root,
+                                 List<String> path,
+                                 Map<String, List<String>> parameters) throws ManagementException
+    {
+        throw createBadRequestManagementException("Preferences not supported for Binding");
+    }
+
+    @Override
+    public void setPreferences(ConfiguredObject<?> root,
+                               List<String> path,
+                               Object preferences,
+                               Map<String, List<String>> parameters,
+                               boolean isPost) throws ManagementException
+    {
+        throw createBadRequestManagementException("Preferences not supported for Binding");
+    }
+
+    @Override
+    public int deletePreferences(ConfiguredObject<?> root,
+                                 List<String> path,
+                                 Map<String, List<String>> parameters) throws ManagementException
+    {
+        throw createBadRequestManagementException("Preferences not supported for Binding");
+    }
+
+    @Override
+    public LegacyConfiguredObject convertFromNextVersion(final LegacyConfiguredObject nextVersionObject)
+    {
+        return null;
+    }
+
+    private Collection<LegacyConfiguredObject> getExchangeBindings(final ConfiguredObject<?> root,
+                                                                   final List<String> exchangePath,
+                                                                   final String queueName,
+                                                                   final String bindingName)
+    {
+        final Object result = getNextVersionExchanges(root, exchangePath);
+        if (result instanceof LegacyConfiguredObject)
+        {
+            return getExchangeBindings((LegacyConfiguredObject) result, queueName, bindingName);
+        }
+        else if (result instanceof Collection)
+        {
+            return ((Collection<?>) result).stream()
+                                           .map(LegacyConfiguredObject.class::cast)
+                                           .map(e -> getExchangeBindings(e, queueName, bindingName))
+                                           .flatMap(Collection::stream)
+                                           .collect(Collectors.toList());
+        }
+        else
+        {
+            throw createInternalServerErrorManagementException("Unexpected format of content from next version");
+        }
+
+    }
+
+    private Object getNextVersionExchanges(final ConfiguredObject<?> root, final List<String> exchangePath)
+    {
+        try
+        {
+            return _nextVersionManagementController.get(root,
+                                                        ExchangeController.TYPE.toLowerCase(),
+                                                        exchangePath,
+                                                        Collections.emptyMap());
+        }
+        catch (ManagementException e)
+        {
+            if (e.getStatusCode() != HttpServletResponse.SC_NOT_FOUND)
+            {
+                throw e;
+            }
+            return Collections.emptyList();
+        }
+    }
+
+
+    private Collection<LegacyConfiguredObject> getExchangeBindings(final LegacyConfiguredObject nextVersionExchange,
+                                                                   final String queueName,
+                                                                   final String bindingName)
+    {
+
+        @SuppressWarnings("unchecked")
+        Object items = nextVersionExchange.getAttribute("bindings");
+        if (items instanceof Collection)
+        {
+            return ((Collection<?>) items).stream()
+                                          .map(Binding.class::cast)
+                                          .filter(b -> (queueName == null
+                                                        || "*".equals(queueName)
+                                                        || queueName.equals(b.getDestination()))
+                                                       && (bindingName == null || "*".equals(bindingName) || bindingName
+                                                  .equals(b.getName())))
+                                          .map(b -> createManageableBinding(b, nextVersionExchange))
+                                          .collect(Collectors.toList());
+        }
+        return Collections.emptyList();
+    }
+
+    private LegacyConfiguredObject createManageableBinding(final Binding binding,
+                                                           final LegacyConfiguredObject nextVersionExchange)
+    {
+        final LegacyConfiguredObject nextVersionVirtualHost = nextVersionExchange.getParent(VirtualHostController.TYPE);
+        final LegacyConfiguredObject queue = findNextVersionQueue(binding.getDestination(), nextVersionVirtualHost);
+
+        return new LegacyBinding(_managementController,
+                                 nextVersionExchange,
+                                 queue,
+                                 binding.getBindingKey(),
+                                 binding.getArguments());
+    }
+
+
+    private static LegacyConfiguredObject findNextVersionQueue(final String queueName,
+                                                               final LegacyConfiguredObject nextVersionVirtualHost)
+    {
+        Collection<LegacyConfiguredObject> queues = nextVersionVirtualHost.getChildren(QueueController.TYPE);
+        return queues.stream()
+                     .filter(q -> queueName.equals(q.getAttribute(GenericLegacyConfiguredObject.NAME)))
+                     .findFirst()
+                     .orElse(null);
+    }
+
+
+    private static UUID generateBindingId(final LegacyConfiguredObject exchange,
+                                          final String queueName,
+                                          final String bindingKey)
+    {
+        String pseudoID = exchange.getAttribute(GenericLegacyConfiguredObject.ID) + "/" + queueName + "/" + bindingKey;
+        return UUID.nameUUIDFromBytes(pseudoID.getBytes(StandardCharsets.UTF_8));
+    }
+
+    static class LegacyBinding implements LegacyConfiguredObject
+    {
+        private static final String ARGUMENTS = "arguments";
+        private static final String QUEUE = "queue";
+        private static final String EXCHANGE = "exchange";
+        private static final Collection<String> ATTRIBUTE_NAMES =
+                Collections.unmodifiableSet(Stream.concat(GenericLegacyConfiguredObject.AVAILABLE_ATTRIBUTES.stream(),
+                                                          Stream.of(ARGUMENTS, QUEUE, EXCHANGE))
+                                                  .collect(Collectors.toSet()));
+
+        private final String _bindingKey;
+        private final Map<String, Object> _arguments;
+        private final LegacyConfiguredObject _exchange;
+        private final UUID _id;
+        private final LegacyConfiguredObject _queue;
+        private final LegacyManagementController _controller;
+
+        LegacyBinding(final LegacyManagementController controller,
+                      final LegacyConfiguredObject nextVersionExchange,
+                      final LegacyConfiguredObject nextVersionQueue,
+                      final String bindingKey,
+                      final Map<String, Object> arguments)
+        {
+            _controller = controller;
+            _exchange = _controller.convertFromNextVersion(nextVersionExchange);
+            _queue = _controller.convertFromNextVersion(nextVersionQueue);
+            _bindingKey = bindingKey;
+            _arguments = arguments != null && !arguments.isEmpty() ? arguments : null;
+            String queueName = (String) nextVersionQueue.getAttribute(NAME);
+            _id = generateBindingId(nextVersionExchange, queueName, bindingKey);
+        }
+
+        @Override
+        public Collection<String> getAttributeNames()
+        {
+            return ATTRIBUTE_NAMES;
+        }
+
+        @Override
+        public Object getAttribute(final String name)
+        {
+            if (ID.equalsIgnoreCase(name))
+            {
+                return _id;
+            }
+            else if (NAME.equalsIgnoreCase(name))
+            {
+                return _bindingKey;
+            }
+            else if (STATE.equalsIgnoreCase(name) || DESIRED_STATE.equalsIgnoreCase(name))
+            {
+                return State.ACTIVE;
+            }
+            else if (TYPE.equalsIgnoreCase(name))
+            {
+                return TYPE;
+            }
+            else if (CONTEXT.equalsIgnoreCase(name))
+            {
+                return _exchange.getAttribute(CONTEXT);
+            }
+            else if (QUEUE.equalsIgnoreCase(name))
+            {
+                return _queue;
+            }
+            else if (EXCHANGE.equalsIgnoreCase(name))
+            {
+                return _exchange;
+            }
+            else if (DURABLE.equalsIgnoreCase(name))
+            {
+                return Boolean.TRUE.equals(_queue.getAttribute(DURABLE))
+                       && Boolean.TRUE.equals(_exchange.getAttribute(DURABLE));
+            }
+            else if (LIFETIME_POLICY.equalsIgnoreCase(name))
+            {
+                return _queue.getAttribute(LIFETIME_POLICY);
+            }
+            else if (ARGUMENTS.equalsIgnoreCase(name))
+            {
+                return _arguments;
+            }
+
+            return null;
+        }
+
+        @Override
+        public Object getActualAttribute(final String name)
+        {
+            if (QUEUE.equalsIgnoreCase(name))
+            {
+                return _queue.getAttribute(LegacyConfiguredObject.NAME);
+            }
+            else if (EXCHANGE.equalsIgnoreCase(name))
+            {
+                return _exchange.getAttribute(LegacyConfiguredObject.NAME);
+            }
+            return getAttribute(name);
+        }
+
+        @Override
+        public Collection<LegacyConfiguredObject> getChildren(final String category)
+        {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public String getCategory()
+        {
+            return BindingController.TYPE;
+        }
+
+        @Override
+        public ManagementResponse invoke(final String operation,
+                                         final Map<String, Object> parameters,
+                                         final boolean isSecure)
+        {
+            if ("getStatistics".equals(operation))
+            {
+                return new ControllerManagementResponse(ResponseType.DATA, Collections.emptyMap());
+            }
+            throw createBadRequestManagementException("No operation is available for Binding");
+        }
+
+        @Override
+        public LegacyConfiguredObject getNextVersionConfiguredObject()
+        {
+            return null;
+        }
+
+        public void delete()
+        {
+            Map<String, Object> parameters = new LinkedHashMap<>();
+            parameters.put("bindingKey", getAttribute(NAME));
+            parameters.put("destination", _queue.getAttribute(NAME));
+            _exchange.getNextVersionConfiguredObject().invoke("unbind", parameters, true);
+        }
+
+        @Override
+        public LegacyConfiguredObject getParent(final String category)
+        {
+            if (QueueController.TYPE.equalsIgnoreCase(category))
+            {
+                return _queue;
+            }
+            else if (ExchangeController.TYPE.equalsIgnoreCase(category))
+            {
+                return _exchange;
+            }
+            throw createInternalServerErrorManagementException(String.format("Category %s is not parent of Binding",
+                                                                             category));
+        }
+
+        @Override
+        public boolean isSecureAttribute(final String name)
+        {
+            return false;
+        }
+
+        @Override
+        public boolean isOversizedAttribute(final String name)
+        {
+            return false;
+        }
+
+        @Override
+        public String getContextValue(final String contextKey)
+        {
+            return _exchange.getContextValue(contextKey);
+        }
+
+        @Override
+        public Map<String, Object> getStatistics()
+        {
+            return Collections.emptyMap();
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BrokerController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BrokerController.java
new file mode 100644
index 0000000..28005df
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BrokerController.java
@@ -0,0 +1,212 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createGoneManagementException;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.ConverterHelper;
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+
+public class BrokerController extends LegacyCategoryController
+{
+    public static final String TYPE = "Broker";
+    private static final String CONNECTION_SESSION_COUNT_LIMIT = "connection.sessionCountLimit";
+    private static final String CONNECTION_HEART_BEAT_DELAY = "connection.heartBeatDelay";
+    private static final String CONNECTION_CLOSE_WHEN_NO_ROUTE = "connection.closeWhenNoRoute";
+
+    private static Map<String, String> BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT = new HashMap<>();
+
+    static
+    {
+        BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.put(CONNECTION_SESSION_COUNT_LIMIT, "qpid.port.sessionCountLimit");
+        BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.put(CONNECTION_HEART_BEAT_DELAY, "qpid.port.heartbeatDelay");
+        BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.put(CONNECTION_CLOSE_WHEN_NO_ROUTE, "qpid.port.closeWhenNoRoute");
+    }
+
+    BrokerController(final LegacyManagementController legacyManagementController,
+                     final Set<TypeController> typeControllers)
+    {
+        super(legacyManagementController,
+              TYPE,
+              new String[0],
+              "Broker",
+              typeControllers);
+    }
+
+    @Override
+    public LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new LegacyBroker(getManagementController(), object);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected Map<String, Object> convertAttributesToNextVersion(final ConfiguredObject<?> root,
+                                                                 final List<String> path,
+                                                                 final Map<String, Object> attributes)
+    {
+        final Map<String, Object> converted = new LinkedHashMap<>(attributes);
+        final Map<String, String> context = (Map<String, String>) converted.get(LegacyConfiguredObject.CONTEXT);
+        final Map<String, String> newContext = new LinkedHashMap<>();
+        if (context != null)
+        {
+            newContext.putAll(context);
+        }
+        converted.put(LegacyConfiguredObject.CONTEXT, newContext);
+        for (String attributeName : BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.keySet())
+        {
+            Object value = converted.remove(attributeName);
+            if (value != null)
+            {
+                newContext.put(BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.get(attributeName), String.valueOf(value));
+            }
+        }
+
+        converted.remove("statisticsReportingResetEnabled");
+
+        final Object statisticsReportingPeriod = converted.get("statisticsReportingPeriod");
+        if (statisticsReportingPeriod != null
+            && !newContext.containsKey("qpid.broker.statisticsReportPattern")
+            && (ConverterHelper.toInt(statisticsReportingPeriod) > 0 || ConverterHelper.isContextVariable(
+                statisticsReportingPeriod)))
+        {
+            newContext.put("qpid.broker.statisticsReportPattern",
+                           "messagesIn=${messagesIn}, bytesIn=${bytesIn:byteunit}, messagesOut=${messagesOut}, bytesOut=${bytesOut:byteunit}");
+        }
+
+        return converted;
+    }
+
+    static class LegacyBroker extends GenericLegacyConfiguredObject
+    {
+        private static final String MODEL_VERSION = "modelVersion";
+
+        LegacyBroker(final LegacyManagementController managementController,
+                     final LegacyConfiguredObject nextVersionLegacyConfiguredObject)
+        {
+            super(managementController, nextVersionLegacyConfiguredObject, BrokerController.TYPE);
+        }
+
+        @Override
+        public Collection<String> getAttributeNames()
+        {
+            return Stream.concat(super.getAttributeNames().stream(),
+                                 BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.keySet().stream()).collect(
+                    Collectors.toSet());
+        }
+
+        @Override
+        public Object getAttribute(final String name)
+        {
+            if (MODEL_VERSION.equals(name))
+            {
+                return getManagementController().getVersion();
+            }
+            else if (BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.containsKey(name))
+            {
+                Object value = getMovedAttribute(name);
+                if (value != null)
+                {
+                    if (CONNECTION_SESSION_COUNT_LIMIT.equals(name))
+                    {
+                        return ConverterHelper.toInt(value);
+                    }
+                    else if (CONNECTION_HEART_BEAT_DELAY.equals(name))
+                    {
+                        return ConverterHelper.toLong(value);
+                    }
+                    else if (CONNECTION_CLOSE_WHEN_NO_ROUTE.equals(name))
+                    {
+                        return ConverterHelper.toBoolean(value);
+                    }
+                }
+                return null;
+            }
+            return super.getAttribute(name);
+        }
+
+        @Override
+        public Object getActualAttribute(final String name)
+        {
+            if (MODEL_VERSION.equals(name))
+            {
+                return getManagementController().getVersion();
+            }
+            else if (BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.containsKey(name))
+            {
+                return getMovedAttribute(name);
+            }
+            return super.getActualAttribute(name);
+        }
+
+        @SuppressWarnings("unchecked")
+        private Object getMovedAttribute(final String name)
+        {
+            Map<String, String> context = (Map<String, String>) super.getAttribute(LegacyConfiguredObject.CONTEXT);
+            if (context != null)
+            {
+                String contextVariable = BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.get(name);
+                return context.get(contextVariable);
+            }
+            return null;
+        }
+
+        @Override
+        public ManagementResponse invoke(final String operation,
+                                         final Map<String, Object> parameters,
+                                         final boolean isSecure)
+        {
+            if ("resetStatistics".equalsIgnoreCase(operation))
+            {
+                throw createGoneManagementException("Method 'resetStatistics' was removed");
+            }
+            return super.invoke(operation, parameters, isSecure);
+        }
+
+        @Override
+        public boolean isSecureAttribute(final String name)
+        {
+            return !BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.containsKey(name) && super.isSecureAttribute(name);
+        }
+
+        @Override
+        public boolean isOversizedAttribute(final String name)
+        {
+            return !BROKER_ATTRIBUTES_MOVED_INTO_CONTEXT.containsKey(name) && super.isOversizedAttribute(name);
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ConsumerController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ConsumerController.java
new file mode 100644
index 0000000..9f215e7
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ConsumerController.java
@@ -0,0 +1,257 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createBadRequestManagementException;
+import static org.apache.qpid.server.management.plugin.ManagementException.createInternalServerErrorManagementException;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public class ConsumerController implements CategoryController
+{
+    public static final String TYPE = "Consumer";
+    private final LegacyManagementController _managementController;
+
+    ConsumerController(final LegacyManagementController managementController)
+    {
+        _managementController = managementController;
+    }
+
+    @Override
+    public String getCategory()
+    {
+        return TYPE;
+    }
+
+    @Override
+    public String getNextVersionCategory()
+    {
+        return TYPE;
+    }
+
+    @Override
+    public String getDefaultType()
+    {
+        return null;
+    }
+
+    @Override
+    public String[] getParentCategories()
+    {
+        return new String[]{SessionController.TYPE, QueueController.TYPE};
+    }
+
+    @Override
+    public LegacyManagementController getManagementController()
+    {
+        return _managementController;
+    }
+
+    private ManagementController getNextVersionManagementController()
+    {
+        return _managementController.getNextVersionManagementController();
+    }
+
+    @Override
+    public Object get(final ConfiguredObject<?> root,
+                      final List<String> path,
+                      final Map<String, List<String>> parameters) throws ManagementException
+    {
+        final Collection<String> hierarchy = _managementController.getCategoryHierarchy(root, TYPE);
+        final String consumerName = path.size() == hierarchy.size() ? path.get(hierarchy.size() - 1) : null;
+        final String queueName = path.size() >= hierarchy.size() - 1 ? path.get(hierarchy.size() - 2) : null;
+        final String sessionName = path.size() >= hierarchy.size() - 2 ? path.get(hierarchy.size() - 3) : null;
+
+        List<String> virtualHostPath = path;
+        if (virtualHostPath.size() > hierarchy.size() - 3)
+        {
+            virtualHostPath = virtualHostPath.subList(0, hierarchy.size() - 3);
+        }
+
+        final Object queues = getNextVersionManagementController().get(root,
+                                                                       "Queue",
+                                                                       virtualHostPath,
+                                                                       Collections.emptyMap());
+
+        Collection<LegacyConfiguredObject> consumers;
+        if (queues instanceof LegacyConfiguredObject)
+        {
+            consumers = getQueueConsumers(sessionName, queueName, consumerName, (LegacyConfiguredObject) queues);
+        }
+        else if (queues instanceof Collection)
+        {
+            consumers = ((Collection<?>) queues).stream()
+                                           .map(LegacyConfiguredObject.class::cast)
+                                           .map(q -> getQueueConsumers(sessionName, queueName, consumerName, q))
+                                           .flatMap(Collection::stream)
+                                           .collect(Collectors.toList());
+        }
+        else
+        {
+            throw createInternalServerErrorManagementException("Unexpected consumer format from next version");
+        }
+        return consumers;
+    }
+
+
+    @Override
+    public int delete(ConfiguredObject<?> root,
+                      List<String> path,
+                      Map<String, List<String>> parameters) throws ManagementException
+    {
+        throw createBadRequestManagementException("Consumer cannot be deleted via management interfaces");
+    }
+
+    @Override
+    public LegacyConfiguredObject createOrUpdate(ConfiguredObject<?> root,
+                                                 List<String> path,
+                                                 Map<String, Object> attributes,
+                                                 boolean isPost) throws ManagementException
+    {
+        throw createBadRequestManagementException("Consumer cannot be created or updated via management interfaces");
+    }
+
+    @Override
+    public ManagementResponse invoke(ConfiguredObject<?> root,
+                                     List<String> path,
+                                     String operation,
+                                     Map<String, Object> parameters,
+                                     boolean isPost, final boolean isSecure) throws ManagementException
+    {
+        Object result = get( root, path, Collections.emptyMap());
+        if (result instanceof Collection && ((Collection)result).size() == 1)
+        {
+            LegacyConfiguredObject  object = (LegacyConfiguredObject) ((Collection<?>)result).iterator().next();
+            return object.invoke(operation, parameters, isSecure);
+        }
+
+        throw createBadRequestManagementException(String.format("Cannot find consumer for path %s",
+                                                                String.join("/" + path)));
+    }
+
+    @Override
+    public Object getPreferences(ConfiguredObject<?> root,
+                                 List<String> path,
+                                 Map<String, List<String>> parameters) throws ManagementException
+    {
+        throw createBadRequestManagementException("Preferences not supported for Consumer");
+    }
+
+    @Override
+    public void setPreferences(ConfiguredObject<?> root,
+                               List<String> path,
+                               Object preferences,
+                               Map<String, List<String>> parameters,
+                               boolean isPost) throws ManagementException
+    {
+        throw createBadRequestManagementException("Preferences not supported for Consumer");
+    }
+
+    @Override
+    public int deletePreferences(ConfiguredObject<?> root,
+                                 List<String> path,
+                                 Map<String, List<String>> parameters) throws ManagementException
+    {
+        throw createBadRequestManagementException("Preferences not supported for Consumer");
+    }
+
+    @Override
+    public LegacyConfiguredObject convertFromNextVersion(final LegacyConfiguredObject nextVersionObject)
+    {
+        return new LegacyConsumer(getManagementController(), nextVersionObject);
+    }
+
+
+    private Collection<LegacyConfiguredObject> getQueueConsumers(final String sessionName,
+                                                                 final String queueName,
+                                                                 final String consumerName,
+                                                                 final LegacyConfiguredObject nextVersionQueue)
+    {
+        if (queueName == null
+            || queueName.equals("*")
+            || queueName.equals(nextVersionQueue.getAttribute(LegacyConfiguredObject.NAME)))
+        {
+            Collection<LegacyConfiguredObject> queueConsumers = nextVersionQueue.getChildren(ConsumerController.TYPE);
+            if (queueConsumers != null)
+            {
+                return queueConsumers.stream()
+                                     .filter(c -> consumerName == null
+                                                  || "*".equals(consumerName)
+                                                  || consumerName.equals(c.getAttribute("name")))
+                                     .filter(c -> {
+                                         if (sessionName == null || "*".equals(sessionName))
+                                         {
+                                             return true;
+                                         }
+                                         else
+                                         {
+                                             Object obj = c.getAttribute("session");
+                                             if (obj instanceof LegacyConfiguredObject)
+                                             {
+                                                 return sessionName.equals(((LegacyConfiguredObject) obj).getAttribute(
+                                                         LegacyConfiguredObject.NAME));
+                                             }
+                                             return false;
+                                         }
+                                     })
+                                     .map(this::convertFromNextVersion)
+                                     .collect(Collectors.toList());
+            }
+        }
+        return Collections.emptyList();
+    }
+
+    static class LegacyConsumer extends GenericLegacyConfiguredObject
+    {
+        LegacyConsumer(final LegacyManagementController managementController,
+                       final LegacyConfiguredObject nextVersionLegacyConfiguredObject)
+        {
+            super(managementController, nextVersionLegacyConfiguredObject, ConsumerController.TYPE);
+        }
+
+        @Override
+        public LegacyConfiguredObject getParent(final String category)
+        {
+            if (SessionController.TYPE.equals(category))
+            {
+                final LegacyConfiguredObject nextVersionConsumer = getNextVersionLegacyConfiguredObject();
+                final LegacyConfiguredObject nextVersionSession =
+                        (LegacyConfiguredObject) nextVersionConsumer.getAttribute(SessionController.TYPE.toLowerCase());
+                return getManagementController().convertFromNextVersion(nextVersionSession);
+            }
+
+            return getManagementController().convertFromNextVersion(getNextVersionLegacyConfiguredObject().getParent(category));
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/DestinationController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/DestinationController.java
new file mode 100644
index 0000000..df70c1b
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/DestinationController.java
@@ -0,0 +1,207 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createBadRequestManagementException;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+import org.apache.qpid.server.model.AlternateBinding;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public class DestinationController extends LegacyCategoryController
+{
+
+    private static final String ALTERNATE_BINDING = "alternateBinding";
+
+    DestinationController(final LegacyManagementController legacyManagementController,
+                          final String name,
+                          final String[] parentCategories,
+                          final String defaultType,
+                          final Set<TypeController> typeControllers)
+    {
+        super(legacyManagementController,
+              name,
+              parentCategories,
+              defaultType,
+              typeControllers);
+    }
+
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> convertAttributesToNextVersion(final ConfiguredObject<?> root,
+                                                              final List<String> path,
+                                                              final Map<String, Object> attributes)
+    {
+        if (attributes.containsKey(LegacyDestination.ALTERNATE_EXCHANGE))
+        {
+            final Map<String, Object> converted = new LinkedHashMap<>(attributes);
+            final String alternateExchange = (String) converted.remove(LegacyDestination.ALTERNATE_EXCHANGE);
+            final LegacyConfiguredObject exchange = findExchange(root,
+                                                                 path,
+                                                                 object -> alternateExchange.equals(object.getAttribute(LegacyConfiguredObject.ID))
+                                                                           || alternateExchange.equals(object.getAttribute(LegacyConfiguredObject.NAME)));
+            if (exchange != null)
+            {
+                converted.put(ALTERNATE_BINDING,
+                              Collections.singletonMap("destination",
+                                                       exchange.getAttribute(LegacyConfiguredObject.NAME)));
+            }
+            else
+            {
+                throw createBadRequestManagementException(String.format("Cannot find alternate exchange '%s'",
+                                                                        alternateExchange));
+            }
+            return converted;
+        }
+        return attributes;
+    }
+
+    private LegacyConfiguredObject findExchange(final ConfiguredObject<?> root,
+                                                final List<String> path,
+                                                final Predicate<LegacyConfiguredObject> predicate)
+    {
+        final Collection<String> hierarchy =
+                getNextVersionManagementController().getCategoryHierarchy(root, ExchangeController.TYPE);
+
+        final List<String> exchangePath = path.size() > hierarchy.size() - 1 ? path.subList(0, hierarchy.size() - 1) : path;
+        final Object result = getNextVersionManagementController().get(root,
+                                                                       ExchangeController.TYPE,
+                                                                       exchangePath,
+                                                                       Collections.emptyMap());
+        if (result instanceof Collection)
+        {
+            final Collection<?> exchanges = (Collection<?>) result;
+            return exchanges.stream()
+                            .filter(LegacyConfiguredObject.class::isInstance)
+                            .map(LegacyConfiguredObject.class::cast)
+                            .filter(predicate)
+                            .findFirst()
+                            .orElse(null);
+        }
+        else
+        {
+            throw createBadRequestManagementException("Cannot find alternate exchange");
+        }
+    }
+
+    public static class LegacyDestination extends GenericLegacyConfiguredObject
+    {
+        static final String ALTERNATE_EXCHANGE = "alternateExchange";
+
+        LegacyDestination(final LegacyManagementController managementController,
+                          final LegacyConfiguredObject nextVersionDestination,
+                          final String category)
+        {
+            super(managementController, nextVersionDestination, category);
+        }
+
+        @Override
+        public Collection<String> getAttributeNames()
+        {
+            return Stream.concat(super.getAttributeNames().stream(),
+                                 Stream.of(ALTERNATE_EXCHANGE)).collect(Collectors.toSet());
+        }
+
+        @Override
+        public Object getAttribute(final String name)
+        {
+            return getAttributeInternal(name, false);
+        }
+
+
+        @Override
+        public Object getActualAttribute(final String name)
+        {
+            return getAttributeInternal(name, true);
+        }
+
+        protected Object getAttributeInternal(final String name, boolean isActual)
+        {
+            if (ALTERNATE_EXCHANGE.equals(name))
+            {
+                final Object altBinding = getAttribute(ALTERNATE_BINDING, isActual);
+                if (altBinding instanceof AlternateBinding)
+                {
+                    final AlternateBinding alternateBinding = (AlternateBinding) altBinding;
+
+                    final Collection<LegacyConfiguredObject> exchanges =
+                            getNextVersionLegacyConfiguredObject().getParent(VirtualHostController.TYPE)
+                                                                  .getChildren(ExchangeController.TYPE);
+
+                    final LegacyConfiguredObject altExchange = exchanges.stream()
+                                                                  .filter(e -> alternateBinding.getDestination()
+                                                                                               .equals(e.getAttribute(NAME)))
+                                                                  .findFirst()
+                                                                  .orElse(null);
+                    if (altExchange != null)
+                    {
+                        return getManagementController().convertFromNextVersion(altExchange);
+                    }
+                }
+                return null;
+            }
+            return getAttribute(name, isActual);
+        }
+
+        Object getAttribute(String name, boolean isActual)
+        {
+            if (isActual)
+            {
+                return super.getActualAttribute(name);
+            }
+            return super.getAttribute(name);
+        }
+
+        @Override
+        public boolean isSecureAttribute(final String name)
+        {
+            if (ALTERNATE_EXCHANGE.equals(name))
+            {
+                return super.isSecureAttribute(ALTERNATE_BINDING);
+            }
+            return super.isSecureAttribute(name);
+        }
+
+        @Override
+        public boolean isOversizedAttribute(final String name)
+        {
+            if (ALTERNATE_EXCHANGE.equals(name))
+            {
+                return super.isOversizedAttribute(ALTERNATE_BINDING);
+            }
+            return super.isOversizedAttribute(name);
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ExchangeController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ExchangeController.java
new file mode 100644
index 0000000..c5a63a1
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ExchangeController.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+import org.apache.qpid.server.model.Binding;
+
+public class ExchangeController extends DestinationController
+{
+
+    public static final String TYPE = "Exchange";
+
+    ExchangeController(final LegacyManagementController legacyManagementController,
+                       final Set<TypeController> typeControllers)
+    {
+        super(legacyManagementController,
+              TYPE,
+              new String[]{VirtualHostController.TYPE},
+              null,
+              typeControllers);
+    }
+
+    @Override
+    protected LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new LegacyExchange(getManagementController(), object);
+    }
+
+    public static class LegacyExchange extends LegacyDestination
+    {
+        LegacyExchange(final LegacyManagementController managementController,
+                       final LegacyConfiguredObject nextVersionQueue)
+        {
+            super(managementController, nextVersionQueue, ExchangeController.TYPE);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Collection<LegacyConfiguredObject> getChildren(final String category)
+        {
+            if (BindingController.TYPE.equals(category))
+            {
+                Collection<Binding> bindings = (Collection<Binding>) getAttribute("bindings");
+                if (bindings != null)
+                {
+                    Map<String, LegacyConfiguredObject> queues =
+                            getNextVersionLegacyConfiguredObject().getParent(VirtualHostController.TYPE)
+                                                                  .getChildren(QueueController.TYPE)
+                                                                  .stream()
+                                                                  .collect(Collectors.toMap(q -> (String) q.getAttribute(
+                                                                          LegacyConfiguredObject.NAME),
+                                                                                            q -> q));
+                    return bindings.stream()
+                                   .map(b -> new BindingController.LegacyBinding(getManagementController(),
+                                                                             getNextVersionLegacyConfiguredObject(),
+                                                                             queues.get(b.getName()),
+                                                                             b.getBindingKey(),
+                                                                             b.getArguments()))
+                                   .collect(Collectors.toList());
+                }
+                return Collections.emptyList();
+            }
+            return super.getChildren(category);
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryController.java
new file mode 100644
index 0000000..fb347f7
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryController.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.controller.GenericCategoryController;
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+
+public class LegacyCategoryController extends GenericCategoryController
+{
+
+    private final String[] _parentCategories;
+
+
+    LegacyCategoryController(final LegacyManagementController managementController,
+                             final String name,
+                             final String[] parentCategories,
+                             final String defaultType,
+                             final Set<TypeController> typeControllers)
+    {
+        super(managementController,
+              managementController.getNextVersionManagementController(),
+              name,
+              defaultType,
+              typeControllers);
+        _parentCategories = parentCategories;
+    }
+
+    @Override
+    public String[] getParentCategories()
+    {
+        return _parentCategories;
+    }
+
+    @Override
+    protected LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new GenericLegacyConfiguredObject(getManagementController(), object, getCategory());
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerFactory.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerFactory.java
new file mode 100644
index 0000000..9f49dab
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerFactory.java
@@ -0,0 +1,268 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.controller.CategoryControllerFactory;
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.v6_1.LegacyManagementControllerFactory;
+import org.apache.qpid.server.plugin.PluggableService;
+
+@PluggableService
+public class LegacyCategoryControllerFactory implements CategoryControllerFactory
+{
+    static final String CATEGORY_BROKER = BrokerController.TYPE;
+    private static final String CATEGORY_BROKER_LOGGER = "BrokerLogger";
+    private static final String CATEGORY_BROKER_LOG_INCLUSION_RULE = "BrokerLogInclusionRule";
+    private static final String CATEGORY_AUTHENTICATION_PROVIDER = "AuthenticationProvider";
+    private static final String CATEGORY_USER = "User";
+    private static final String CATEGORY_ACCESS_CONTROL_PROVIDER = "AccessControlProvider";
+    private static final String CATEGORY_PLUGIN = "Plugin";
+    private static final String CATEGORY_TRUST_STORE = "TrustStore";
+    private static final String CATEGORY_KEY_STORE = "KeyStore";
+    static final String CATEGORY_PORT = PortController.TYPE;
+    private static final String CATEGORY_VIRTUAL_HOST_ALIAS = "VirtualHostAlias";
+    private static final String CATEGORY_GROUP_PROVIDER = "GroupProvider";
+    private static final String CATEGORY_GROUP = "Group";
+    private static final String CATEGORY_GROUP_MEMBER = "GroupMember";
+    static final String CATEGORY_VIRTUAL_HOST_NODE = "VirtualHostNode";
+    private static final String CATEGORY_REMOTE_REPLICATION_NODE = "RemoteReplicationNode";
+    static final String CATEGORY_VIRTUAL_HOST = VirtualHostController.TYPE;
+    private static final String CATEGORY_VIRTUAL_HOST_LOGGER = "VirtualHostLogger";
+    private static final String CATEGORY_VIRTUAL_HOST_LOG_INCLUSION_RULE = "VirtualHostLogInclusionRule";
+    private static final String CATEGORY_VIRTUAL_HOST_ACCESS_CONTROL_PROVIDER = "VirtualHostAccessControlProvider";
+    static final String CATEGORY_EXCHANGE = ExchangeController.TYPE;
+    static final String CATEGORY_QUEUE = QueueController.TYPE;
+    private static final String CATEGORY_BINDING = BindingController.TYPE;
+    static final String CATEGORY_CONSUMER = ConsumerController.TYPE;
+    static final String CATEGORY_CONNECTION = "Connection";
+    static final String CATEGORY_SESSION = SessionController.TYPE;
+    static final Set<String> SUPPORTED_CATEGORIES =
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CATEGORY_BROKER,
+                                                                    CATEGORY_BROKER_LOGGER,
+                                                                    CATEGORY_BROKER_LOG_INCLUSION_RULE,
+                                                                    CATEGORY_AUTHENTICATION_PROVIDER,
+                                                                    CATEGORY_USER,
+                                                                    CATEGORY_ACCESS_CONTROL_PROVIDER,
+                                                                    CATEGORY_PLUGIN,
+                                                                    CATEGORY_TRUST_STORE,
+                                                                    CATEGORY_KEY_STORE,
+                                                                    CATEGORY_PORT,
+                                                                    CATEGORY_VIRTUAL_HOST_ALIAS,
+                                                                    CATEGORY_GROUP_PROVIDER,
+                                                                    CATEGORY_GROUP,
+                                                                    CATEGORY_GROUP_MEMBER,
+                                                                    CATEGORY_VIRTUAL_HOST_NODE,
+                                                                    CATEGORY_REMOTE_REPLICATION_NODE,
+                                                                    CATEGORY_VIRTUAL_HOST,
+                                                                    CATEGORY_VIRTUAL_HOST_LOGGER,
+                                                                    CATEGORY_VIRTUAL_HOST_LOG_INCLUSION_RULE,
+                                                                    CATEGORY_VIRTUAL_HOST_ACCESS_CONTROL_PROVIDER,
+                                                                    CATEGORY_EXCHANGE,
+                                                                    CATEGORY_QUEUE,
+                                                                    CATEGORY_BINDING,
+                                                                    CATEGORY_CONSUMER,
+                                                                    CATEGORY_CONNECTION,
+                                                                    CATEGORY_SESSION
+                                                                   )));
+
+    private static final Map<String, String> DEFAULT_TYPES = Collections.unmodifiableMap(new HashMap<String, String>()
+    {
+        {
+            put(CATEGORY_BROKER_LOGGER, "Broker");
+            put(CATEGORY_TRUST_STORE, "FileTrustStore");
+            put(CATEGORY_KEY_STORE, "FileKeyStore");
+            put(CATEGORY_GROUP, "ManagedGroup");
+            put(CATEGORY_GROUP_MEMBER, "ManagedGroupMember");
+            put(CATEGORY_VIRTUAL_HOST, "ProvidedStore");
+            put(CATEGORY_QUEUE, "standard");
+        }
+    });
+
+    @Override
+    public CategoryController createController(final String type,
+                                               final LegacyManagementController legacyManagementController)
+    {
+        switch (type)
+        {
+            case CATEGORY_ACCESS_CONTROL_PROVIDER:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_BROKER:
+                return new BrokerController(legacyManagementController,
+                                            legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_BROKER_LOGGER:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_BROKER_LOG_INCLUSION_RULE:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER_LOGGER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_AUTHENTICATION_PROVIDER:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_USER:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_AUTHENTICATION_PROVIDER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_PORT:
+                return new PortController(legacyManagementController,
+                                          legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_VIRTUAL_HOST_ALIAS:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_VIRTUAL_HOST_ALIAS},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_PLUGIN:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_TRUST_STORE:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_KEY_STORE:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_GROUP_PROVIDER:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_GROUP:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_GROUP_PROVIDER},
+                                                    null,
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_GROUP_MEMBER:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_GROUP},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_VIRTUAL_HOST_NODE:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_BROKER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_REMOTE_REPLICATION_NODE:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_VIRTUAL_HOST_NODE},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_VIRTUAL_HOST:
+                return new VirtualHostController(legacyManagementController,
+                                                 legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_VIRTUAL_HOST_LOGGER:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_VIRTUAL_HOST},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_VIRTUAL_HOST_LOG_INCLUSION_RULE:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_VIRTUAL_HOST_LOGGER},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_VIRTUAL_HOST_ACCESS_CONTROL_PROVIDER:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_VIRTUAL_HOST},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_EXCHANGE:
+                return new ExchangeController(legacyManagementController,
+                                              legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_QUEUE:
+                return new QueueController(legacyManagementController,
+                                           legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_BINDING:
+                return new BindingController(legacyManagementController
+                );
+            case CATEGORY_CONNECTION:
+                return new LegacyCategoryController(legacyManagementController,
+                                                    type,
+                                                    new String[]{CATEGORY_PORT, CATEGORY_VIRTUAL_HOST},
+                                                    DEFAULT_TYPES.get(type),
+                                                    legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_SESSION:
+                return new SessionController(legacyManagementController,
+                                             legacyManagementController.getTypeControllersByCategory(type));
+            case CATEGORY_CONSUMER:
+                return new ConsumerController(legacyManagementController
+                );
+            default:
+                throw new IllegalArgumentException(String.format("Unsupported category '%s'", type));
+        }
+    }
+
+    @Override
+    public Set<String> getSupportedCategories()
+    {
+        return SUPPORTED_CATEGORIES;
+    }
+
+    @Override
+    public String getModelVersion()
+    {
+        return LegacyManagementControllerFactory.MODEL_VERSION;
+    }
+
+    @Override
+    public String getType()
+    {
+        return LegacyCategoryControllerFactory.class.getName();
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/PortController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/PortController.java
new file mode 100644
index 0000000..1564b74
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/PortController.java
@@ -0,0 +1,140 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public class PortController extends LegacyCategoryController
+{
+    public static final String TYPE = "Port";
+
+    PortController(final LegacyManagementController legacyManagementController,
+                   final Set<TypeController> typeControllers)
+    {
+        super(legacyManagementController,
+              TYPE,
+              new String[]{BrokerController.TYPE},
+              null,
+              typeControllers);
+    }
+
+    @Override
+    protected LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new LegacyPort(getManagementController(), object);
+    }
+
+    @Override
+    public Map<String, Object> convertAttributesToNextVersion(final ConfiguredObject<?> root,
+                                                              final List<String> path,
+                                                              final Map<String, Object> attributes)
+    {
+        Map<String, Object> portAttributes = attributes;
+        if ("HTTP".equals(portAttributes.get("type")) && portAttributes.containsKey("context"))
+        {
+            @SuppressWarnings("unchecked")
+            Map<String, String> context = (Map<String, String>) portAttributes.get("context");
+            if (context.containsKey("port.http.additionalInternalThreads")
+                || context.containsKey("port.http.maximumQueuedRequests"))
+            {
+                Map<String, Object> updatedAttributes = new LinkedHashMap<>(portAttributes);
+                updatedAttributes.put("context", convertContextToNextVersion(context));
+                portAttributes = updatedAttributes;
+            }
+        }
+        return portAttributes;
+    }
+
+    private Map<String, String> convertContextToNextVersion(final Map<String, String> context)
+            throws ManagementException
+    {
+        if (context.containsKey("port.http.additionalInternalThreads")
+            || context.containsKey("port.http.maximumQueuedRequests"))
+        {
+            Map<String, String> updatedContext = new LinkedHashMap<>(context);
+            updatedContext.remove("port.http.additionalInternalThreads");
+            String acceptorsBacklog = updatedContext.remove("port.http.maximumQueuedRequests");
+            if (acceptorsBacklog != null)
+            {
+                updatedContext.put("qpid.port.http.acceptBacklog", acceptorsBacklog);
+            }
+            return updatedContext;
+        }
+        return context;
+    }
+
+    static class LegacyPort extends GenericLegacyConfiguredObject
+    {
+        LegacyPort(final LegacyManagementController managementController,
+                   final LegacyConfiguredObject nextVersionQueue)
+        {
+            super(managementController, nextVersionQueue, PortController.TYPE);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Object getAttribute(final String name)
+        {
+            if (CONTEXT.equals(name))
+            {
+                if ("HTTP".equals(super.getAttribute(TYPE)))
+                {
+                    Map<String, String> context = (Map<String, String>) super.getAttribute(CONTEXT);
+                    return convertContextIfRequired(context);
+                }
+            }
+            return super.getAttribute(name);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Object getActualAttribute(final String name)
+        {
+            if (CONTEXT.equals(name) && "HTTP".equals(super.getAttribute(TYPE)))
+            {
+                Map<String, String> context = (Map<String, String>) super.getActualAttribute(CONTEXT);
+                return convertContextIfRequired(context);
+            }
+            return super.getActualAttribute(name);
+        }
+
+        private Object convertContextIfRequired(final Map<String, String> context)
+        {
+            if (context != null && context.containsKey("qpid.port.http.acceptBacklog"))
+            {
+                Map<String, String> updatedContext = new LinkedHashMap<>(context);
+                updatedContext.put("port.http.maximumQueuedRequests", updatedContext.remove("qpid.port.http.acceptBacklog"));
+                return updatedContext;
+            }
+            return context;
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/QueueController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/QueueController.java
new file mode 100644
index 0000000..10d6400
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/QueueController.java
@@ -0,0 +1,273 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.controller.v6_1.category;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.qpid.server.management.plugin.controller.ConverterHelper;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+public class QueueController extends DestinationController
+{
+    public static final String TYPE = "Queue";
+
+    QueueController(final LegacyManagementController legacyManagementController,
+                    final Set<TypeController> typeControllers)
+    {
+        super(legacyManagementController,
+              TYPE,
+              new String[]{VirtualHostController.TYPE},
+              "standard",
+              typeControllers);
+    }
+
+    @Override
+    protected LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new LegacyQueue(getManagementController(), object);
+    }
+
+    @Override
+    public Map<String, Object> convertAttributesToNextVersion(final ConfiguredObject<?> root,
+                                                              final List<String> path,
+                                                              final Map<String, Object> attributes)
+    {
+        Map<String, Object> converted = new LinkedHashMap<>(attributes);
+        Object queueFlowControlSizeBytes = converted.remove("queueFlowControlSizeBytes");
+        Object queueFlowResumeSizeBytes = converted.remove("queueFlowResumeSizeBytes");
+        if (queueFlowControlSizeBytes != null)
+        {
+            long queueFlowControlSizeBytesValue = ConverterHelper.toLong(queueFlowControlSizeBytes);
+            if (queueFlowControlSizeBytesValue > 0)
+            {
+                if (queueFlowResumeSizeBytes != null)
+                {
+                    long queueFlowResumeSizeBytesValue = ConverterHelper.toLong(queueFlowResumeSizeBytes);
+                    double ratio = ((double) queueFlowResumeSizeBytesValue)
+                                   / ((double) queueFlowControlSizeBytesValue);
+                    String flowResumeLimit = String.format("%.2f", ratio * 100.0);
+
+                    Object context = converted.get("context");
+                    Map<String, String> contextMap;
+                    if (context instanceof Map)
+                    {
+                        contextMap = (Map) context;
+                    }
+                    else
+                    {
+                        contextMap = new LinkedHashMap<>();
+                        converted.put("context", contextMap);
+                    }
+                    contextMap.put("queue.queueFlowResumeLimit", flowResumeLimit);
+                }
+                converted.put("overflowPolicy", "PRODUCER_FLOW_CONTROL");
+                converted.put("maximumQueueDepthBytes", queueFlowControlSizeBytes);
+            }
+        }
+
+        if (converted.containsKey("messageGroupKey"))
+        {
+            if (converted.containsKey("messageGroupSharedGroups")
+                && ConverterHelper.toBoolean(converted.remove("messageGroupSharedGroups")))
+            {
+                converted.put("messageGroupType", "SHARED_GROUPS");
+            }
+            else
+            {
+                converted.put("messageGroupType", "STANDARD");
+            }
+            Object oldMessageGroupKey = converted.remove("messageGroupKey");
+            if (!"JMSXGroupId".equals(oldMessageGroupKey))
+            {
+                converted.put("messageGroupKeyOverride", oldMessageGroupKey);
+            }
+        }
+        else
+        {
+            converted.put("messageGroupType", "NONE");
+        }
+
+        return super.convertAttributesToNextVersion(root, path, converted);
+    }
+
+
+    static class LegacyQueue extends LegacyDestination
+    {
+        static final String QUEUE_FLOW_RESUME_SIZE_BYTES = "queueFlowResumeSizeBytes";
+        static final String QUEUE_FLOW_CONTROL_SIZE_BYTES = "queueFlowControlSizeBytes";
+        static final String MESSAGE_GROUP_SHARED_GROUPS = "messageGroupSharedGroups";
+        static final String MESSAGE_GROUP_KEY = "messageGroupKey";
+
+        Set<String> MOVED_ATTRIBUTES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+                QUEUE_FLOW_RESUME_SIZE_BYTES,
+                QUEUE_FLOW_CONTROL_SIZE_BYTES,
+                MESSAGE_GROUP_SHARED_GROUPS,
+                MESSAGE_GROUP_KEY,
+                ALTERNATE_EXCHANGE)));
+
+        LegacyQueue(final LegacyManagementController managementController,
+                    final LegacyConfiguredObject nextVersionQueue)
+        {
+            super(managementController, nextVersionQueue, QueueController.TYPE);
+        }
+
+        @Override
+        public Collection<String> getAttributeNames()
+        {
+            return Stream.concat(super.getAttributeNames().stream(),
+                                 MOVED_ATTRIBUTES.stream()).collect(Collectors.toSet());
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Collection<LegacyConfiguredObject> getChildren(final String category)
+        {
+            if (BindingController.TYPE.equals(category))
+            {
+                String queueName = (String) getAttribute(NAME);
+                Collection<LegacyConfiguredObject> exchanges =
+                        getNextVersionLegacyConfiguredObject().getParent(VirtualHostController.TYPE)
+                                                              .getChildren(ExchangeController.TYPE);
+
+                List<LegacyConfiguredObject> bindingObjects = new ArrayList<>();
+                for (LegacyConfiguredObject exchange : exchanges)
+                {
+                    Object bindings = exchange.getAttribute("bindings");
+                    if (bindings instanceof Collection)
+                    {
+                        Collection<?> exchangeBindings = (Collection<?>) bindings;
+                        exchangeBindings.stream()
+                                        .map(Binding.class::cast)
+                                        .filter(i -> i.getDestination().equals(queueName))
+                                        .map(i -> new BindingController.LegacyBinding(getManagementController(),
+                                                                                      exchange,
+                                                                                      getNextVersionLegacyConfiguredObject(),
+                                                                                      i.getBindingKey(),
+                                                                                      i.getArguments()))
+                                        .forEach(bindingObjects::add);
+                    }
+                }
+                return bindingObjects;
+            }
+            return super.getChildren(category);
+        }
+
+        protected Object getAttributeInternal(final String name, boolean isActual)
+        {
+            if (QUEUE_FLOW_CONTROL_SIZE_BYTES.equals(name))
+            {
+                final Object overflowPolicy = getAttribute("overflowPolicy", isActual);
+                if (overflowPolicy != null)
+                {
+                    final Object maximumQueueDepthBytes = getAttribute("maximumQueueDepthBytes", isActual);
+                    if ("PRODUCER_FLOW_CONTROL".equals(String.valueOf(overflowPolicy))
+                        && maximumQueueDepthBytes != null)
+                    {
+                        return maximumQueueDepthBytes;
+                    }
+                }
+                return null;
+            }
+            else if (QUEUE_FLOW_RESUME_SIZE_BYTES.equals(name))
+            {
+                final Object overflowPolicy = getAttribute("overflowPolicy", isActual);
+                final Object maximumQueueDepthBytes = getAttribute("maximumQueueDepthBytes", isActual);
+                if ("PRODUCER_FLOW_CONTROL".equals(String.valueOf(overflowPolicy)) && maximumQueueDepthBytes != null)
+                {
+                    final long queueFlowControlSizeBytesValue = ConverterHelper.toLong(maximumQueueDepthBytes);
+                    if (queueFlowControlSizeBytesValue > 0)
+                    {
+                        int queueFlowResumeLimit = 80;
+
+                        @SuppressWarnings("unchecked")
+                        Map<String, String> context = (Map<String, String>) super.getAttribute(CONTEXT);
+                        if (context != null)
+                        {
+                            queueFlowResumeLimit = ConverterHelper.toInt(context.get("queue.queueFlowResumeLimit"));
+                            if (queueFlowResumeLimit == 0)
+                            {
+                                queueFlowResumeLimit = 80;
+                            }
+                        }
+                        return Math.round(queueFlowControlSizeBytesValue * ((double) queueFlowResumeLimit) / 100.0);
+                    }
+                }
+                return null;
+            }
+            else if (MESSAGE_GROUP_SHARED_GROUPS.equals(name))
+            {
+                Object messageGroupType = getAttribute("messageGroupType", isActual);
+                if (messageGroupType != null)
+                {
+                    String type = String.valueOf(messageGroupType);
+                    return "SHARED_GROUPS".equals(type);
+                }
+                return null;
+            }
+            else if (MESSAGE_GROUP_KEY.equals(name))
+            {
+                Object messageGroupKeyOverride = getAttribute("messageGroupKeyOverride", isActual);
+                Object messageGroupType = getAttribute("messageGroupType", isActual);
+                if (messageGroupType != null)
+                {
+                    return messageGroupKeyOverride == null ? "JMSXGroupId" : messageGroupKeyOverride;
+                }
+                return null;
+            }
+
+            return super.getAttributeInternal(name, isActual);
+        }
+
+        @Override
+        public boolean isSecureAttribute(final String name)
+        {
+             if (MOVED_ATTRIBUTES.contains(name))
+             {
+                 return false;
+             }
+             return super.isSecureAttribute(name);
+        }
+
+        @Override
+        public boolean isOversizedAttribute(final String name)
+        {
+            if (MOVED_ATTRIBUTES.contains(name))
+            {
+                return false;
+            }
+            return super.isOversizedAttribute(name);
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/SessionController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/SessionController.java
new file mode 100644
index 0000000..5177b1e
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/SessionController.java
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+
+public class SessionController extends LegacyCategoryController
+{
+    public static final String TYPE = "Session";
+
+    SessionController(final LegacyManagementController legacyManagementController,
+                      final Set<TypeController> typeControllers)
+    {
+        super(legacyManagementController,
+              TYPE,
+              new String[]{LegacyCategoryControllerFactory.CATEGORY_CONNECTION},
+              null,
+              typeControllers);
+    }
+
+    @Override
+    protected LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new LegacySession(getManagementController(), object);
+    }
+
+    public static class LegacySession extends GenericLegacyConfiguredObject
+    {
+        LegacySession(final LegacyManagementController managementController,
+                      final LegacyConfiguredObject nextVersionLegacyConfiguredObject)
+        {
+            super(managementController, nextVersionLegacyConfiguredObject, SessionController.TYPE);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public Collection<LegacyConfiguredObject> getChildren(final String category)
+        {
+            if (ConsumerController.TYPE.equalsIgnoreCase(category))
+            {
+                final LegacyConfiguredObject nextVersionSession = getNextVersionLegacyConfiguredObject();
+                final LegacyConfiguredObject connection =
+                        nextVersionSession.getParent(LegacyCategoryControllerFactory.CATEGORY_CONNECTION);
+                final LegacyConfiguredObject vh = connection.getParent(VirtualHostController.TYPE);
+                final UUID sessionID = (UUID) getAttribute(ID);
+                final UUID connectionID = (UUID) connection.getAttribute(ID);
+                final List<LegacyConfiguredObject> consumers = new ArrayList<>();
+                final Collection<LegacyConfiguredObject> queues = vh.getChildren(QueueController.TYPE);
+                if (queues != null)
+                {
+                    queues.forEach(q -> {
+                        final Collection<LegacyConfiguredObject> queueConsumers =
+                                q.getChildren(ConsumerController.TYPE);
+                        if (queueConsumers != null)
+                        {
+                            queueConsumers.stream()
+                                          .filter(c -> sameSession(c, sessionID, connectionID))
+                                          .map(c -> getManagementController().convertFromNextVersion(c))
+                                          .forEach(consumers::add);
+                        }
+                    });
+                }
+                return consumers;
+            }
+            return super.getChildren(category);
+        }
+
+        private boolean sameSession(final LegacyConfiguredObject consumer,
+                                    final UUID sessionID,
+                                    final UUID connectionID)
+        {
+            LegacyConfiguredObject session = (LegacyConfiguredObject) consumer.getAttribute("session");
+            if (session != null)
+            {
+                if (sessionID.equals(session.getAttribute(ID)))
+                {
+                    LegacyConfiguredObject con = session.getParent(LegacyCategoryControllerFactory.CATEGORY_CONNECTION);
+                    return con != null && connectionID.equals(con.getAttribute(ID));
+                }
+            }
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/VirtualHostController.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/VirtualHostController.java
new file mode 100644
index 0000000..85904d0
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/VirtualHostController.java
@@ -0,0 +1,130 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.apache.qpid.server.management.plugin.ManagementException.createGoneManagementException;
+import static org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject.CONTEXT;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.GenericLegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+import org.apache.qpid.server.model.ConfiguredObject;
+
+class VirtualHostController extends LegacyCategoryController
+{
+
+    public static final String TYPE = "VirtualHost";
+
+    VirtualHostController(final LegacyManagementController legacyManagementController,
+                          final Set<TypeController> typeControllers)
+    {
+        super(legacyManagementController,
+              TYPE,
+              new String[]{LegacyCategoryControllerFactory.CATEGORY_VIRTUAL_HOST_NODE},
+              "ProvidedStore",
+              typeControllers);
+    }
+
+    @Override
+    public LegacyConfiguredObject convertNextVersionLegacyConfiguredObject(final LegacyConfiguredObject object)
+    {
+        return new VirtualHostController.LegacyVirtualHost(getManagementController(), object);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Map<String, Object> convertAttributesToNextVersion(final ConfiguredObject<?> root,
+                                                              final List<String> path,
+                                                              final Map<String, Object> attributes)
+    {
+
+        final Map<String, String> context = (Map<String, String>) attributes.get(CONTEXT);
+
+        if (attributes.containsKey(LegacyVirtualHost.QUEUE_DEAD_LETTER_QUEUE_ENABLED)
+            || (context != null && context.containsKey("queue.deadLetterQueueEnabled")))
+        {
+            final Map<String, Object> converted = new LinkedHashMap<>(attributes);
+            converted.remove("queue_deadLetterQueueEnabled");
+            if (context != null)
+            {
+                final Map<String, String> convertedContext = new LinkedHashMap<>(context);
+                converted.put("context", convertedContext);
+                convertedContext.remove("queue.deadLetterQueueEnabled");
+            }
+
+            return converted;
+        }
+        return attributes;
+    }
+
+    public static class LegacyVirtualHost extends GenericLegacyConfiguredObject
+    {
+        static final String QUEUE_DEAD_LETTER_QUEUE_ENABLED = "queue_deadLetterQueueEnabled";
+        private static final String MODEL_VERSION = "modelVersion";
+
+        LegacyVirtualHost(final LegacyManagementController managementController,
+                          final LegacyConfiguredObject nextVersionLegacyConfiguredObject)
+        {
+            super(managementController, nextVersionLegacyConfiguredObject, VirtualHostController.TYPE);
+        }
+
+        @Override
+        public Collection<String> getAttributeNames()
+        {
+
+            return super.getAttributeNames();
+        }
+
+        @Override
+        public Object getAttribute(final String name)
+        {
+            if (MODEL_VERSION.equals(name))
+            {
+                return getManagementController().getVersion();
+            }
+            else if (QUEUE_DEAD_LETTER_QUEUE_ENABLED.equals(name))
+            {
+                return false;
+            }
+            return super.getAttribute(name);
+        }
+
+        @Override
+        public ManagementResponse invoke(final String operation,
+                                         final Map<String, Object> parameters,
+                                         final boolean isSecure)
+        {
+            if ("resetStatistics".equalsIgnoreCase(operation))
+            {
+                throw createGoneManagementException("Method 'resetStatistics' was removed");
+            }
+            return super.invoke(operation, parameters, isSecure);
+        }
+    }
+}
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementControllerTest.java
new file mode 100644
index 0000000..50a8815
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/LegacyManagementControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+import org.apache.qpid.server.management.plugin.controller.CategoryControllerFactory;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.TypeController;
+import org.apache.qpid.server.management.plugin.controller.TypeControllerFactory;
+import org.apache.qpid.server.management.plugin.controller.v6_1.category.BrokerController;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class LegacyManagementControllerTest extends UnitTestBase
+{
+
+    private LegacyManagementController _controller;
+    private ManagementController _nextVersionManagementController;
+
+    @Before
+    public void setUp()
+    {
+        _nextVersionManagementController = mock(ManagementController.class);
+        _controller = new LegacyManagementController(_nextVersionManagementController);
+        _controller.initialize();
+    }
+
+    @Test
+    public void convertQueryParameters()
+    {
+        final Map<String, List<String>> parameters = Collections.singletonMap("actuals", Collections.singletonList("true"));
+        final Map<String, List<String>> converted = _controller.convertQueryParameters(parameters);
+        assertThat(converted, is(notNullValue()));
+        assertThat(converted.get("excludeInheritedContext"), is(equalTo(Collections.singletonList("true"))));
+    }
+
+    @Test
+    public void formatConfiguredObject()
+    {
+        final String objectName = "test-object";
+        final String hostName = "test-vhn";
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+        final LegacyConfiguredObject object = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject vhn = mock(LegacyConfiguredObject.class);
+        when(object.getAttributeNames()).thenReturn(Arrays.asList(LegacyConfiguredObject.NAME,
+                                                                  LegacyConfiguredObject.TYPE));
+        when(object.getAttribute(LegacyConfiguredObject.NAME)).thenReturn(objectName);
+        when(object.getAttribute(LegacyConfiguredObject.TYPE)).thenReturn("Broker");
+        when(object.getCategory()).thenReturn("Broker");
+        when(object.getChildren("VirtualHostNode")).thenReturn(Collections.singletonList(vhn));
+        when(vhn.getAttributeNames()).thenReturn(Arrays.asList(LegacyConfiguredObject.NAME,
+                                                               LegacyConfiguredObject.TYPE));
+        when(vhn.getAttribute(LegacyConfiguredObject.NAME)).thenReturn(hostName);
+        when(vhn.getAttribute(LegacyConfiguredObject.TYPE)).thenReturn("VirtualHostNode");
+        when(vhn.getCategory()).thenReturn("VirtualHostNode");
+
+        Object data = _controller.formatConfiguredObject(object, parameters, true);
+
+        assertThat(data, is(instanceOf(Map.class)));
+        Map<?, ?> formatted = (Map<?, ?>) data;
+
+        assertThat(formatted.get(LegacyConfiguredObject.NAME), is(equalTo(objectName)));
+        assertThat(formatted.get(LegacyConfiguredObject.TYPE), is(equalTo("Broker")));
+
+        Object vhns = formatted.get("virtualhostnodes");
+        assertThat(vhns, is(instanceOf(Collection.class)));
+
+        Collection<?> nodes = (Collection<?>)vhns;
+
+        assertThat(nodes.size(), is(equalTo(1)));
+
+        Object node = nodes.iterator().next();
+        assertThat(node, is(instanceOf(Map.class)));
+        Map<?, ?> formattedNode = (Map<?, ?>) node;
+
+        assertThat(formattedNode.get(LegacyConfiguredObject.NAME), is(equalTo(hostName)));
+        assertThat(formattedNode.get(LegacyConfiguredObject.TYPE), is(equalTo("VirtualHostNode")));
+    }
+
+    @Test
+    public void get()
+    {
+        final List<String> path = Collections.emptyList();
+        final LegacyConfiguredObject object = mock(LegacyConfiguredObject.class);
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+
+        final ConfiguredObject<?> root = mock(ConfiguredObject.class);
+        when(_nextVersionManagementController.get(eq(root), eq(BrokerController.TYPE), eq(path), any())).thenReturn(object);
+        final Object result = _controller.get(root, BrokerController.TYPE, path, parameters);
+        assertThat(result, is(instanceOf(Collection.class)));
+        Collection data = (Collection) result;
+        assertThat(data.size(), is(equalTo(1)));
+        Object obj = data.iterator().next();
+        assertThat(obj, is(instanceOf(LegacyConfiguredObject.class)));
+
+        assertThat(((LegacyConfiguredObject)obj).getCategory(), is(equalTo(BrokerController.TYPE)));
+    }
+
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BindingControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BindingControllerTest.java
new file mode 100644
index 0000000..0051b60
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BindingControllerTest.java
@@ -0,0 +1,312 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+import org.apache.qpid.server.management.plugin.controller.ControllerManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class BindingControllerTest extends UnitTestBase
+{
+    private LegacyManagementController _managementController;
+    private LegacyManagementController _nextVersionManagementController;
+    private BindingController _controller;
+    private ConfiguredObject<?> _root;
+
+    @Before
+    public void setUp()
+    {
+        _managementController = mock(LegacyManagementController.class);
+        _nextVersionManagementController = mock(LegacyManagementController.class);
+        when(_managementController.getNextVersionManagementController()).thenReturn(_nextVersionManagementController);
+        _controller = new BindingController(_managementController);
+        _root = mock(ConfiguredObject.class);
+    }
+
+    @Test
+    public void getCategory()
+    {
+        assertThat(_controller.getCategory(), is(equalTo("Binding")));
+    }
+
+    @Test
+    public void getNextVersionCategory()
+    {
+        assertThat(_controller.getNextVersionCategory(), is(equalTo(null)));
+    }
+
+    @Test
+    public void getDefaultType()
+    {
+        assertThat(_controller.getDefaultType(), is(equalTo(null)));
+    }
+
+    @Test
+    public void getParentCategories()
+    {
+        assertThat(_controller.getParentCategories(), is(equalTo(new String[]{"Exchange", "Queue"})));
+    }
+
+    @Test
+    public void getManagementController()
+    {
+        assertThat(_controller.getManagementController(), is(equalTo(_managementController)));
+    }
+
+    @Test
+    public void get()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh", "my-exchange", "my-queue", "my-binding");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("actuals", Collections.singletonList("true"));
+
+        final List<String> hierarchy = Arrays.asList("virtualhostnode", "virtualhost", "exchange", "queue", "binding");
+        when(_managementController.getCategoryHierarchy(_root, "Binding")).thenReturn(hierarchy);
+
+        final LegacyConfiguredObject exchange1 = mock(LegacyConfiguredObject.class);
+        when(exchange1.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("foo");
+        when(exchange1.getCategory()).thenReturn("Exchange");
+
+        final LegacyConfiguredObject exchange2 = mock(LegacyConfiguredObject.class);
+        when(exchange2.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-exchange");
+        when(exchange2.getCategory()).thenReturn("Exchange");
+        final LegacyConfiguredObject vh = mock(LegacyConfiguredObject.class);
+        when(exchange2.getParent("VirtualHost")).thenReturn(vh);
+        final LegacyConfiguredObject queue = mock(LegacyConfiguredObject.class);
+        when(queue.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-queue");
+        final Collection<LegacyConfiguredObject> queues = Collections.singletonList(queue);
+        when(vh.getChildren("Queue")).thenReturn(queues);
+        final Binding binding = mock(Binding.class);
+        when(binding.getName()).thenReturn("my-binding");
+        when(binding.getDestination()).thenReturn("my-queue");
+        when(binding.getBindingKey()).thenReturn("my-binding");
+        final Collection<Binding> bindings = Collections.singletonList(binding);
+        when(exchange2.getAttribute("bindings")).thenReturn(bindings);
+        final Collection<LegacyConfiguredObject> exchanges = Arrays.asList(exchange1, exchange2);
+
+        doReturn(exchanges).when(_nextVersionManagementController).get(any(), eq("exchange"), any(), any());
+
+        final Object readResult = _controller.get(_root, path, parameters);
+        assertThat(readResult, is(instanceOf(Collection.class)));
+
+        final Collection<?> exchangeBindings = (Collection<?>) readResult;
+        assertThat(exchangeBindings.size(), is(equalTo(1)));
+
+        final Object object = exchangeBindings.iterator().next();
+        assertThat(object, is(instanceOf(LegacyConfiguredObject.class)));
+
+        final LegacyConfiguredObject bindingObject = (LegacyConfiguredObject) object;
+        assertThat(bindingObject.getAttribute(LegacyConfiguredObject.NAME), is(equalTo("my-binding")));
+    }
+
+    @Test
+    public void createOrUpdate()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh", "my-exchange");
+        final Map<String, Object> attributes = new HashMap<>();
+        attributes.put("name", "my-binding");
+        attributes.put("queue", "my-queue");
+
+        final List<String> hierarchy = Arrays.asList("virtualhostnode", "virtualhost", "exchange", "queue", "binding");
+        doReturn(hierarchy).when(_managementController).getCategoryHierarchy(_root, "Binding");
+
+        final LegacyConfiguredObject exchange = mock(LegacyConfiguredObject.class);
+        when(exchange.getCategory()).thenReturn("Exchange");
+        when(exchange.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-exchange");
+
+        final ManagementResponse bindingResult = new ControllerManagementResponse(ResponseType.DATA, Boolean.TRUE);
+        when(exchange.invoke(eq("bind"), any(), eq(true))).thenReturn(bindingResult);
+
+        final LegacyConfiguredObject queue = mock(LegacyConfiguredObject.class);
+        when(queue.getCategory()).thenReturn("Queue");
+        when(queue.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-queue");
+
+        doReturn(exchange).when(_nextVersionManagementController).get(any(), eq("exchange"), any(), any());
+        doReturn(queue).when(_nextVersionManagementController).get(any(), eq("queue"), any(), any());
+        when(_managementController.convertFromNextVersion(exchange)).thenReturn(exchange);
+        when(_managementController.convertFromNextVersion(queue)).thenReturn(queue);
+
+        final LegacyConfiguredObject binding = _controller.createOrUpdate(_root, path, attributes, true);
+        assertThat(binding, is(notNullValue()));
+
+        assertThat(binding.getAttribute(LegacyConfiguredObject.NAME), is(equalTo("my-binding")));
+
+        Object queueObject = binding.getAttribute("queue");
+        Object exchangeObject = binding.getAttribute("exchange");
+
+        assertThat(queueObject, is(instanceOf(LegacyConfiguredObject.class)));
+        assertThat(exchangeObject, is(instanceOf(LegacyConfiguredObject.class)));
+
+        assertThat(((LegacyConfiguredObject) queueObject).getAttribute(LegacyConfiguredObject.NAME),
+                   is(equalTo("my-queue")));
+        assertThat(((LegacyConfiguredObject) exchangeObject).getAttribute(LegacyConfiguredObject.NAME),
+                   is(equalTo("my-exchange")));
+    }
+
+    @Test
+    public void delete()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh", "my-exchange", "my-queue", "my-binding");
+
+        final List<String> hierarchy = Arrays.asList("virtualhostnode", "virtualhost", "exchange", "queue", "binding");
+        doReturn(hierarchy).when(_managementController).getCategoryHierarchy(_root, "Binding");
+
+
+        final LegacyConfiguredObject exchange = mock(LegacyConfiguredObject.class);
+        when(exchange.getCategory()).thenReturn("Exchange");
+        when(exchange.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-exchange");
+
+        final ManagementResponse unbindingResult = new ControllerManagementResponse(ResponseType.DATA, Boolean.TRUE);
+        when(exchange.invoke(eq("unbind"), any(), eq(true))).thenReturn(unbindingResult);
+
+        doReturn(exchange).when(_nextVersionManagementController).get(any(), eq("exchange"), any(), any());
+
+        int result = _controller.delete(_root, path, Collections.emptyMap());
+
+        assertThat(result, is(equalTo(1)));
+        verify(exchange).invoke(eq("unbind"), any(), eq(true));
+    }
+
+    @Test
+    public void invoke()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh", "my-exchange", "my-queue", "my-binding");
+        final String operationName = "getStatistics";
+        final Map<String, Object> parameters = Collections.emptyMap();
+
+
+
+        final List<String> hierarchy = Arrays.asList("virtualhostnode", "virtualhost", "exchange", "queue", "binding");
+        when(_managementController.getCategoryHierarchy(_root, "Binding")).thenReturn(hierarchy);
+
+
+        final LegacyConfiguredObject exchange = mock(LegacyConfiguredObject.class);
+        when(exchange.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-exchange");
+        when(exchange.getCategory()).thenReturn("Exchange");
+        final LegacyConfiguredObject vh = mock(LegacyConfiguredObject.class);
+        when(exchange.getParent("VirtualHost")).thenReturn(vh);
+        final LegacyConfiguredObject queue = mock(LegacyConfiguredObject.class);
+        when(queue.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-queue");
+        final Collection<LegacyConfiguredObject> queues = Collections.singletonList(queue);
+        when(vh.getChildren("Queue")).thenReturn(queues);
+        final Binding binding = mock(Binding.class);
+        when(binding.getName()).thenReturn("my-binding");
+        when(binding.getDestination()).thenReturn("my-queue");
+        when(binding.getBindingKey()).thenReturn("my-binding");
+        final Collection<Binding> bindings = Collections.singletonList(binding);
+        when(exchange.getAttribute("bindings")).thenReturn(bindings);
+        final Collection<LegacyConfiguredObject> exchanges = Collections.singletonList(exchange);
+
+        doReturn(exchanges).when(_nextVersionManagementController).get(any(), eq("exchange"), any(), any());
+
+        final ManagementResponse result = _controller.invoke(_root, path, operationName, parameters, true, true);
+
+        assertThat(result, is(notNullValue()));
+        assertThat(result.getResponseCode(), is(equalTo(200)));
+        assertThat(result.getBody(), is(notNullValue()));
+        assertThat(result.getBody(), is(equalTo(Collections.emptyMap())));
+    }
+
+    @Test
+    public void getPreferences()
+    {
+        final List<String> path = Arrays.asList("vhn", "vh", "exchange", "queue", "binding");
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+        try
+        {
+            _controller.getPreferences(_root, path, parameters);
+            fail("Binding preferences are unknown");
+        }
+        catch (ManagementException e)
+        {
+            // pass
+        }
+    }
+
+    @Test
+    public void setPreferences()
+    {
+        final List<String> path = Arrays.asList("vhn", "vh", "exchange", "queue", "binding");
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+        try
+        {
+            _controller.setPreferences(_root,
+                                       path,
+                                       Collections.singletonMap("Binding-Preferences",
+                                                                Collections.singleton(Collections.singletonMap("value",
+                                                                                                               "foo"))),
+                                       parameters,
+                                       true);
+            fail("Binding preferences are unknown");
+        }
+        catch (ManagementException e)
+        {
+            // pass
+        }
+    }
+
+    @Test
+    public void deletePreferences()
+    {
+        final List<String> path = Arrays.asList("vhn", "vh", "exchange", "queue", "binding");
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+        try
+        {
+            _controller.deletePreferences(_root,
+                                          path,
+                                          parameters);
+            fail("Binding preferences are unknown");
+        }
+        catch (ManagementException e)
+        {
+            // pass
+        }
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BrokerControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BrokerControllerTest.java
new file mode 100644
index 0000000..275567e
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/BrokerControllerTest.java
@@ -0,0 +1,104 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class BrokerControllerTest extends UnitTestBase
+{
+    private LegacyManagementController _legacyVersionManagementController;
+    private String MODEL_VERSION = "6.1";
+
+    @Before
+    public void setUp()
+    {
+        _legacyVersionManagementController = mock(LegacyManagementController.class);
+        when(_legacyVersionManagementController.getVersion()).thenReturn(MODEL_VERSION);
+    }
+
+    @Test
+    public void convertNextVersionLegacyConfiguredObject()
+    {
+        final LegacyConfiguredObject object = mock(LegacyConfiguredObject.class);
+        when(object.getAttribute("modelVersion")).thenReturn("foo");
+        final Map<String, String> context = new HashMap<>();
+        context.put("qpid.port.sessionCountLimit", "512");
+        context.put("qpid.port.heartbeatDelay", "10000");
+        context.put("qpid.port.closeWhenNoRoute", "true");
+        when(object.getAttribute("context")).thenReturn(context);
+
+        final BrokerController controller =
+                new BrokerController(_legacyVersionManagementController, Collections.emptySet());
+        assertThat(controller.getCategory(), is(equalTo("Broker")));
+
+        final LegacyConfiguredObject converted = controller.convertFromNextVersion(object);
+        assertThat(converted.getAttribute("modelVersion"), is(equalTo(MODEL_VERSION)));
+        assertThat(converted.getAttribute("connection.sessionCountLimit"), is(equalTo(512)));
+        assertThat(converted.getAttribute("connection.heartBeatDelay"), is(equalTo(10000L)));
+        assertThat(converted.getAttribute("connection.closeWhenNoRoute"), is(equalTo(true)));
+    }
+
+
+    @Test
+    public void convertAttributesToNextVersion()
+    {
+        final Map<String, Object> attributes = new HashMap<>();
+        attributes.put("connection.sessionCountLimit", 512);
+        attributes.put("connection.heartBeatDelay", 10000L);
+        attributes.put("connection.closeWhenNoRoute", true);
+        attributes.put("statisticsReportingResetEnabled", true);
+        attributes.put("statisticsReportingEnabled", true);
+
+        final BrokerController controller =
+                new BrokerController(_legacyVersionManagementController, Collections.emptySet());
+        assertThat(controller.getCategory(), is(equalTo("Broker")));
+
+        final Map<String, Object> converted = controller.convertAttributesToNextVersion(mock(ConfiguredObject.class),
+                                                                                        Collections.emptyList(),
+                                                                                        attributes);
+        assertThat(converted.size(), is(equalTo(2)));
+        assertThat(converted.get("statisticsReportingEnabled"), is(equalTo(true)));
+
+        final Map<String, String> expectedContext = new HashMap<>();
+        expectedContext.put("qpid.port.sessionCountLimit", "512");
+        expectedContext.put("qpid.port.heartbeatDelay", "10000");
+        expectedContext.put("qpid.port.closeWhenNoRoute", "true");
+        assertThat(converted.get("context"), is(equalTo(expectedContext)));
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ConsumerControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ConsumerControllerTest.java
new file mode 100644
index 0000000..4df2929
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ConsumerControllerTest.java
@@ -0,0 +1,317 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementException;
+import org.apache.qpid.server.management.plugin.ManagementResponse;
+import org.apache.qpid.server.management.plugin.ResponseType;
+import org.apache.qpid.server.management.plugin.controller.ControllerManagementResponse;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Connection;
+import org.apache.qpid.server.model.Session;
+import org.apache.qpid.server.transport.AbstractAMQPConnection;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class ConsumerControllerTest extends UnitTestBase
+{
+    private LegacyManagementController _managementController;
+    private LegacyManagementController _nextVersionManagementController;
+    private ConsumerController _controller;
+    private ConfiguredObject<?> _root;
+
+    @Before
+    public void setUp()
+    {
+        _managementController = mock(LegacyManagementController.class);
+        _nextVersionManagementController = mock(LegacyManagementController.class);
+        when(_managementController.getNextVersionManagementController()).thenReturn(_nextVersionManagementController);
+        _controller = new ConsumerController(_managementController);
+        _root = mock(ConfiguredObject.class);
+    }
+    @Test
+    public void getCategory()
+    {
+        assertThat(_controller.getCategory(), is(equalTo("Consumer")));
+    }
+
+    @Test
+    public void getNextVersionCategory()
+    {
+        assertThat(_controller.getNextVersionCategory(), is(equalTo("Consumer")));
+    }
+
+    @Test
+    public void getDefaultType()
+    {
+        assertThat(_controller.getDefaultType(), is(equalTo(null)));
+    }
+
+    @Test
+    public void getParentCategories()
+    {
+        assertThat(_controller.getParentCategories(), is(equalTo(new String[]{"Session", "Queue"})));
+    }
+
+    @Test
+    public void getManagementController()
+    {
+        assertThat(_controller.getManagementController(), is(equalTo(_managementController)));
+    }
+
+    @Test
+    public void get()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh", "my-session", "my-queue", "my-consumer");
+        final Map<String, List<String>> parameters =
+                Collections.singletonMap("actuals", Collections.singletonList("true"));
+
+        final List<String> hierarchy = Arrays.asList("virtualhostnode", "virtualhost", "session", "queue", "consumer");
+        when(_managementController.getCategoryHierarchy(_root, "Consumer")).thenReturn(hierarchy);
+
+        final LegacyConfiguredObject session = mock(LegacyConfiguredObject.class);
+        when(session.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-session");
+        when(session.getCategory()).thenReturn("Session");
+
+        final LegacyConfiguredObject consumer = mock(LegacyConfiguredObject.class);
+        when(consumer.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-consumer");
+        when(consumer.getAttribute("session")).thenReturn(session);
+        final Collection<LegacyConfiguredObject> consumers = Collections.singletonList(consumer);
+
+        final LegacyConfiguredObject queue1 = mock(LegacyConfiguredObject.class);
+        when(queue1.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-queue1");
+        final LegacyConfiguredObject queue2 = mock(LegacyConfiguredObject.class);
+        when(queue2.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-queue");
+        when(queue2.getChildren(ConsumerController.TYPE)).thenReturn(consumers);
+
+        final Collection<LegacyConfiguredObject> queues = Arrays.asList(queue1, queue2);
+
+        doReturn(queues).when(_nextVersionManagementController)
+                       .get(eq(_root),
+                            eq("Queue"),
+                            eq(Arrays.asList("my-vhn", "my-vh")),
+                            eq(Collections.emptyMap()));
+
+        final Object result = _controller.get(_root, path, parameters);
+        assertThat(result, is(instanceOf(Collection.class)));
+
+        Collection<?> consumerItems = (Collection<?>)result;
+        assertThat(consumerItems.size(), is(equalTo(1)));
+
+        final Object object = consumerItems.iterator().next();
+        assertThat(object, is(instanceOf(LegacyConfiguredObject.class)));
+
+        final LegacyConfiguredObject consumerObject = (LegacyConfiguredObject) object;
+        assertThat(consumerObject.getAttribute(LegacyConfiguredObject.NAME), is(equalTo("my-consumer")));
+        assertThat(consumerObject.getCategory(), is(equalTo("Consumer")));
+    }
+
+    @Test
+    public void delete()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh",  "my-queue", "my-consumer");
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+        try
+        {
+            _controller.delete(_root, path, parameters);
+            fail("Consumer cannot be deleted from REST");
+        }
+        catch (ManagementException e)
+        {
+            // pass
+        }
+    }
+
+    @Test
+    public void createOrUpdate()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh",  "my-queue", "my-consumer");
+        final Map<String, Object> attributes = Collections.singletonMap(LegacyConfiguredObject.NAME, "my-consumer" );
+        try
+        {
+            _controller.createOrUpdate(_root, path, attributes, true);
+            fail("Consumer cannot be created from REST");
+        }
+        catch (ManagementException e)
+        {
+            // pass
+        }
+    }
+
+    @Test
+    public void invoke()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh", "my-session", "my-queue", "my-consumer");
+        final List<String> hierarchy = Arrays.asList("virtualhostnode", "virtualhost", "session", "queue", "consumer");
+        when(_managementController.getCategoryHierarchy(_root, "Consumer")).thenReturn(hierarchy);
+
+        final LegacyConfiguredObject consumer = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject queue = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject session = mock(LegacyConfiguredObject.class);
+        when(queue.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-queue");
+        when(consumer.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-consumer");
+        when(consumer.getAttribute("session")).thenReturn(session);
+        when(session.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-session");
+
+        final Object stats = mock(Object.class);
+        final ManagementResponse statistics = new ControllerManagementResponse(ResponseType.DATA, stats);
+        when(consumer.invoke(eq("getStatistics"), eq(Collections.emptyMap()), eq(false))).thenReturn(statistics);
+        final Collection<LegacyConfiguredObject> consumers = Collections.singletonList(consumer);
+        when(queue.getChildren(ConsumerController.TYPE)).thenReturn(consumers);
+
+        final Collection<LegacyConfiguredObject> queues = Collections.singletonList(queue);
+
+        doReturn(queues).when(_nextVersionManagementController)
+                        .get(eq(_root),
+                             eq("Queue"),
+                             eq(Arrays.asList("my-vhn", "my-vh")),
+                             eq(Collections.emptyMap()));
+
+        ManagementResponse response = _controller.invoke(_root, path, "getStatistics", Collections.emptyMap(), false, false);
+        assertThat(response, is(notNullValue()));
+        assertThat(response.getResponseCode(), is(equalTo(200)));
+        assertThat(response.getBody(), is(notNullValue()));
+        assertThat(response.getBody(), is(equalTo(stats)));
+    }
+
+    @Test
+    public void getPreferences()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh",  "my-queue", "my-consumer");
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+        try
+        {
+            _controller.getPreferences(_root, path, parameters);
+            fail("Consumer preferences are unknown");
+        }
+        catch (ManagementException e)
+        {
+            // pass
+        }
+    }
+
+    @Test
+    public void setPreferences()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh",  "my-queue", "my-consumer");
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+        try
+        {
+            _controller.setPreferences(_root,
+                                       path,
+                                       Collections.singletonMap("Consumer-Preferences",
+                                                                Collections.singleton(Collections.singletonMap("value",
+                                                                                                               "foo"))),
+                                       parameters,
+                                       true);
+            fail("Consumer preferences are unknown");
+        }
+        catch (ManagementException e)
+        {
+            // pass
+        }
+    }
+
+    @Test
+    public void deletePreferences()
+    {
+        final List<String> path = Arrays.asList("my-vhn", "my-vh",  "my-queue", "my-consumer");
+        final Map<String, List<String>> parameters = Collections.emptyMap();
+        try
+        {
+            _controller.deletePreferences(_root,
+                                          path,
+                                          parameters);
+            fail("Consumer preferences are unknown");
+        }
+        catch (ManagementException e)
+        {
+            // pass
+        }
+    }
+
+    @Test
+    public void convertFromNextVersion()
+    {
+        final LegacyConfiguredObject session = mock(LegacyConfiguredObject.class);
+        when(session.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-session");
+        when(session.getCategory()).thenReturn("Session");
+
+
+
+        final LegacyConfiguredObject queue1 = mock(LegacyConfiguredObject.class);
+        when(queue1.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-queue1");
+        final LegacyConfiguredObject queue2 = mock(LegacyConfiguredObject.class);
+        when(queue2.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("my-queue");
+
+
+
+        final LegacyConfiguredObject nextVersionConfiguredObject = mock(LegacyConfiguredObject.class);
+        when(nextVersionConfiguredObject.getAttribute("session")).thenReturn(session);
+        when(nextVersionConfiguredObject.getAttribute("queue")).thenReturn(queue2);
+        when(nextVersionConfiguredObject.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("test-consumer");
+        when(nextVersionConfiguredObject.getParent(eq("Queue"))).thenReturn(queue2);
+        final Collection<LegacyConfiguredObject> consumers = Collections.singletonList(nextVersionConfiguredObject);
+        when(queue2.getChildren(ConsumerController.TYPE)).thenReturn(consumers);
+
+        final LegacyConfiguredObject convertedSession = mock(LegacyConfiguredObject.class);
+        when(_managementController.convertFromNextVersion(session)).thenReturn(convertedSession);
+        final LegacyConfiguredObject convertedQueue = mock(LegacyConfiguredObject.class);
+        when(_managementController.convertFromNextVersion(queue2)).thenReturn(convertedQueue);
+        final LegacyConfiguredObject  converted = _controller.convertFromNextVersion(nextVersionConfiguredObject);
+
+        LegacyConfiguredObject sessionParent = converted.getParent("Session");
+        LegacyConfiguredObject queueParent = converted.getParent("Queue");
+
+        assertThat(sessionParent, is(equalTo(convertedSession)));
+        assertThat(queueParent, is(equalTo(convertedQueue)));
+
+        LegacyConfiguredObject sessionAttribute = (LegacyConfiguredObject)converted.getAttribute("session");
+        LegacyConfiguredObject queueAttribute = (LegacyConfiguredObject)converted.getAttribute("queue");
+
+        assertThat(sessionAttribute, is(equalTo(convertedSession)));
+        assertThat(queueAttribute, is(equalTo(convertedQueue)));
+
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/DestinationControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/DestinationControllerTest.java
new file mode 100644
index 0000000..4c76ccf
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/DestinationControllerTest.java
@@ -0,0 +1,135 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockingDetails;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.AlternateBinding;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class DestinationControllerTest extends UnitTestBase
+{
+    private LegacyManagementController _legacyVersionManagementController;
+    private ManagementController _nextVersionManagementController;
+
+    @Before
+    public void setUp()
+    {
+        _legacyVersionManagementController = mock(LegacyManagementController.class);
+        _nextVersionManagementController = mock(ManagementController.class);
+        when(_legacyVersionManagementController.getNextVersionManagementController()).thenReturn(
+                _nextVersionManagementController);
+    }
+
+    @Test
+    public void convertAttributesToNextVersion()
+    {
+        final String alternateExchangeName = "alternate";
+        final String queueName = "test";
+        final Map<String, Object> attributes = new HashMap<>();
+        attributes.put("alternateExchange", alternateExchangeName);
+        attributes.put(LegacyConfiguredObject.NAME, queueName);
+
+        final DestinationController controller =
+                new DestinationController(_legacyVersionManagementController,
+                                          "Queue",
+                                          new String[]{"VirtualHost"},
+                                          null,
+                                          Collections.emptySet());
+        assertThat(controller.getCategory(), is(equalTo("Queue")));
+        assertThat(controller.getParentCategories(), is(equalTo(new String[]{"VirtualHost"})));
+
+        final ConfiguredObject root = mock(ConfiguredObject.class);
+        final List<String> exchangePath = Arrays.asList("vhn", "vh");
+        final LegacyConfiguredObject exchange = mock(LegacyConfiguredObject.class);
+        when(exchange.getAttribute(LegacyConfiguredObject.NAME)).thenReturn(alternateExchangeName);
+        when(exchange.getCategory()).thenReturn(ExchangeController.TYPE);
+        final Collection<LegacyConfiguredObject> exchanges = Collections.singletonList(exchange);
+        when(_nextVersionManagementController.get(eq(root),
+                                                  eq(ExchangeController.TYPE),
+                                                  eq(exchangePath),
+                                                  eq(Collections.emptyMap()))).thenReturn(exchanges);
+
+        final Collection<String> hierarchy = Arrays.asList("virtualhostnode", "virtualhost", "exchange");
+        when(_nextVersionManagementController.getCategoryHierarchy(eq(root), eq(ExchangeController.TYPE))).thenReturn(hierarchy);
+        final List<String> path = Arrays.asList("vhn", "vh", queueName);
+        final Map<String, Object> converted = controller.convertAttributesToNextVersion(root,
+                                                                                        path,
+                                                                                        attributes);
+        assertThat(converted.size(), is(equalTo(2)));
+        assertThat(converted.get(LegacyConfiguredObject.NAME), is(equalTo(queueName)));
+        final Object alternateBinding = converted.get("alternateBinding");
+        assertThat(alternateBinding, is(notNullValue()));
+        assertThat(alternateBinding, is(instanceOf(Map.class)));
+
+        final Map<?, ?> alternateDestination = (Map<?, ?>) alternateBinding;
+        assertThat(alternateDestination.get("destination"), is(equalTo(alternateExchangeName)));
+        assertThat(alternateDestination.get("attributes"), is(equalTo(null)));
+    }
+
+    @Test
+    public void testLegacyDestination()
+    {
+        final String alternateExchangeName = "alt";
+        final LegacyConfiguredObject nextVersionDestination = mock(LegacyConfiguredObject.class);
+        final AlternateBinding alternateDestination = mock(AlternateBinding.class);
+        when(alternateDestination.getDestination()).thenReturn(alternateExchangeName);
+        when(nextVersionDestination.getAttribute("alternateBinding")).thenReturn(alternateDestination);
+
+        final LegacyConfiguredObject vh = mock(LegacyConfiguredObject.class);
+        when(nextVersionDestination.getParent(VirtualHostController.TYPE)).thenReturn(vh);
+        final LegacyConfiguredObject alternateExchange = mock(LegacyConfiguredObject.class);
+        when(alternateExchange.getCategory()).thenReturn(ExchangeController.TYPE);
+        when(alternateExchange.getAttribute(LegacyConfiguredObject.NAME)).thenReturn(alternateExchangeName);
+        final Collection<LegacyConfiguredObject> exchanges = Collections.singletonList(alternateExchange);
+        when(vh.getChildren(ExchangeController.TYPE)).thenReturn(exchanges);
+
+        final LegacyConfiguredObject converted = mock(LegacyConfiguredObject.class);
+        when(_legacyVersionManagementController.convertFromNextVersion(alternateExchange)).thenReturn(converted);
+
+        DestinationController.LegacyDestination destination = new DestinationController.LegacyDestination(_legacyVersionManagementController, nextVersionDestination, "Queue");
+
+
+        assertThat(destination.getAttribute("alternateExchange"), is(equalTo(converted)));
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ExchangeControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ExchangeControllerTest.java
new file mode 100644
index 0000000..13f3ee9
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/ExchangeControllerTest.java
@@ -0,0 +1,113 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.AbstractConfiguredObject;
+import org.apache.qpid.server.model.AlternateBinding;
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class ExchangeControllerTest extends UnitTestBase
+{
+    private LegacyManagementController _legacyManagementController;
+
+    @Before
+    public void setUp()
+    {
+        _legacyManagementController = mock(LegacyManagementController.class);
+        final ManagementController nextVersionManagementController = mock(ManagementController.class);
+        when(_legacyManagementController.getNextVersionManagementController()).thenReturn(
+                nextVersionManagementController);
+    }
+
+    @Test
+    public void convertNextVersionLegacyConfiguredObject()
+    {
+        final ExchangeController exchangeController =
+                new ExchangeController(_legacyManagementController, Collections.emptySet());
+
+        final String exchangeName = "testExchange";
+        final String alternateExchangeName = "altExchange";
+        final String queueName = "testQueue";
+        final String bindingKey = "testBindingKey";
+
+        final LegacyConfiguredObject nextVersionExchange = mock(LegacyConfiguredObject.class);
+        final AlternateBinding alternateBinding = mock(AlternateBinding.class);
+        final Binding nextVersionBinding = mock(Binding.class);
+        final LegacyConfiguredObject nextVersionAlternateExchange = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject nextVersionVirtualHost = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject queue = mock(LegacyConfiguredObject.class);
+
+        when(alternateBinding.getDestination()).thenReturn(alternateExchangeName);
+
+        when(nextVersionExchange.getCategory()).thenReturn(ExchangeController.TYPE);
+        when(nextVersionExchange.getAttribute("alternateBinding")).thenReturn(alternateBinding);
+        when(nextVersionExchange.getAttribute(AbstractConfiguredObject.NAME)).thenReturn(exchangeName);
+        when(nextVersionExchange.getAttribute("bindings")).thenReturn(Collections.singletonList(nextVersionBinding));
+        when(nextVersionExchange.getParent(VirtualHostController.TYPE)).thenReturn(nextVersionVirtualHost);
+
+        when(nextVersionBinding.getDestination()).thenReturn(queueName);
+        when(nextVersionBinding.getBindingKey()).thenReturn(bindingKey);
+
+        when(nextVersionAlternateExchange.getCategory()).thenReturn(ExchangeController.TYPE);
+        when(nextVersionAlternateExchange.getAttribute(LegacyConfiguredObject.NAME)).thenReturn(alternateExchangeName);
+
+        when(nextVersionVirtualHost.getChildren(ExchangeController.TYPE)).thenReturn(Arrays.asList(nextVersionExchange,
+                nextVersionAlternateExchange));
+        when(nextVersionVirtualHost.getChildren(QueueController.TYPE)).thenReturn(Collections.singletonList(queue));
+
+        final LegacyConfiguredObject convertedExchange = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject convertedAltExchange = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject convertedQueue = mock(LegacyConfiguredObject.class);
+        when(_legacyManagementController.convertFromNextVersion(nextVersionExchange)).thenReturn(convertedExchange);
+        when(_legacyManagementController.convertFromNextVersion(nextVersionAlternateExchange)).thenReturn(convertedAltExchange);
+        when(_legacyManagementController.convertFromNextVersion(queue)).thenReturn(convertedQueue);
+
+        final LegacyConfiguredObject destination = exchangeController.convertFromNextVersion(nextVersionExchange);
+
+        assertThat(destination.getAttribute("alternateExchange"), is(equalTo(convertedAltExchange)));
+
+        final Collection<LegacyConfiguredObject> children = destination.getChildren(BindingController.TYPE);
+        assertThat(children.size(), is(equalTo(1)));
+
+        final LegacyConfiguredObject o = children.iterator().next();
+        assertThat(o.getCategory(), is(equalTo(BindingController.TYPE)));
+        assertThat(o.getAttribute(AbstractConfiguredObject.NAME), is(equalTo(bindingKey)));
+        assertThat(o.getAttribute("queue"), is(equalTo(convertedQueue)));
+        assertThat(o.getAttribute("exchange"), is(equalTo(convertedExchange)));
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerFactoryTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerFactoryTest.java
new file mode 100644
index 0000000..db5f26d
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerFactoryTest.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.controller.CategoryController;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class LegacyCategoryControllerFactoryTest extends UnitTestBase
+{
+    private LegacyCategoryControllerFactory _factory;
+    private LegacyManagementController _nextVersionManagementController;
+
+    @Before
+    public void setUp()
+    {
+        _nextVersionManagementController = mock(LegacyManagementController.class);
+        _factory = new LegacyCategoryControllerFactory();
+    }
+
+    @Test
+    public void createController()
+    {
+        LegacyCategoryControllerFactory.SUPPORTED_CATEGORIES.forEach(category -> {
+            final CategoryController controller =
+                    _factory.createController(category, _nextVersionManagementController);
+            assertThat(controller.getCategory(), is(equalTo(category)));
+        });
+    }
+
+    @Test
+    public void getSupportedCategories()
+    {
+        assertThat(_factory.getSupportedCategories(),
+                   is(equalTo(LegacyCategoryControllerFactory.SUPPORTED_CATEGORIES)));
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerTest.java
new file mode 100644
index 0000000..8aa5b0a
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/LegacyCategoryControllerTest.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.VirtualHostNode;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class LegacyCategoryControllerTest extends UnitTestBase
+{
+
+    private LegacyCategoryController _controller;
+
+    @Before
+    public void setUp()
+    {
+        final LegacyManagementController legacyManagementController = mock(LegacyManagementController.class);
+        final ManagementController nextVersionManagementController = mock(ManagementController.class);
+        when(legacyManagementController.getNextVersionManagementController()).thenReturn(
+                nextVersionManagementController);
+        _controller = new LegacyCategoryController(legacyManagementController,
+                                                   VirtualHostNode.TYPE,
+                                                   new String[]{BrokerController.TYPE},
+                                                   null,
+                                                   Collections.emptySet());
+    }
+
+
+    @Test
+    public void getParentCategories()
+    {
+        assertThat(_controller.getParentCategories(), is(equalTo(new String[]{BrokerController.TYPE})));
+    }
+
+    @Test
+    public void convertNextVersionLegacyConfiguredObject()
+    {
+        final LegacyConfiguredObject node = mock(LegacyConfiguredObject.class);
+        when(node.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("test");
+
+        final LegacyConfiguredObject converted =
+                _controller.convertNextVersionLegacyConfiguredObject(node);
+
+        assertThat(converted, is(notNullValue()));
+        assertThat(converted.getCategory(), is(equalTo(VirtualHostNode.TYPE)));
+        assertThat(converted.getAttribute(LegacyConfiguredObject.NAME), is(equalTo("test")));
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/PortControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/PortControllerTest.java
new file mode 100644
index 0000000..a915619
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/PortControllerTest.java
@@ -0,0 +1,113 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class PortControllerTest extends UnitTestBase
+{
+    private PortController _portController;
+
+    @Before
+    public void setUp()
+    {
+        final LegacyManagementController legacyManagementController = mock(LegacyManagementController.class);
+        final ManagementController nextVersionManagementController = mock(ManagementController.class);
+        when(legacyManagementController.getNextVersionManagementController()).thenReturn(
+                nextVersionManagementController);
+        _portController = new PortController(legacyManagementController, Collections.emptySet());
+    }
+
+    @Test
+    public void convertNextVersionLegacyConfiguredObject()
+    {
+        final LegacyConfiguredObject nextVersionPort = mock(LegacyConfiguredObject.class);
+        when(nextVersionPort.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("test");
+        when(nextVersionPort.getAttribute(LegacyConfiguredObject.TYPE)).thenReturn("HTTP");
+        Map<String, String> context = new HashMap<>();
+        context.put("qpid.port.http.acceptBacklog", "2000");
+        when(nextVersionPort.getAttribute(LegacyConfiguredObject.CONTEXT)).thenReturn(context);
+
+        final LegacyConfiguredObject converted =
+                _portController.convertNextVersionLegacyConfiguredObject(nextVersionPort);
+
+        assertThat(converted, is(notNullValue()));
+        assertThat(converted.getCategory(), is(equalTo(PortController.TYPE)));
+        assertThat(converted.getAttribute(LegacyConfiguredObject.NAME), is(equalTo("test")));
+        assertThat(converted.getAttribute(LegacyConfiguredObject.TYPE), is(equalTo("HTTP")));
+
+        Object contextObject = converted.getAttribute(LegacyConfiguredObject.CONTEXT);
+        assertThat(contextObject, is(instanceOf(Map.class)));
+
+        Map<?,?> convertedContext = (Map<?,?>)contextObject;
+        assertThat(convertedContext.get("port.http.maximumQueuedRequests"), is(equalTo("2000")));
+    }
+
+    @Test
+    public void convertAttributesToNextVersion()
+    {
+        Map<String, Object> attributes = new HashMap<>();
+        attributes.put(LegacyConfiguredObject.NAME, "test");
+        attributes.put(LegacyConfiguredObject.TYPE, "HTTP");
+        Map<String, String> context = new HashMap<>();
+        context.put("port.http.maximumQueuedRequests", "2000");
+        context.put("port.http.additionalInternalThreads", "10");
+        attributes.put(LegacyConfiguredObject.CONTEXT, context);
+
+        final ConfiguredObject<?> root = mock(ConfiguredObject.class);
+        final List<String> path = Collections.emptyList();
+        final Map<String, Object> converted = _portController.convertAttributesToNextVersion(root, path, attributes);
+
+        assertThat(converted, is(instanceOf(Map.class)));
+        assertThat(converted.get(LegacyConfiguredObject.NAME), is(equalTo("test")));
+        assertThat(converted.get(LegacyConfiguredObject.TYPE), is(equalTo("HTTP")));
+
+        Object contextObject = converted.get(LegacyConfiguredObject.CONTEXT);
+        assertThat(converted, is(instanceOf(Map.class)));
+
+        Map<?,?> convertedContext = (Map<?,?>)contextObject;
+
+        assertThat(convertedContext.get("qpid.port.http.acceptBacklog"), is(equalTo("2000")));
+        assertThat(convertedContext.containsKey("port.http.additionalInternalThreads"), is(equalTo(false)));
+
+
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/QueueControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/QueueControllerTest.java
new file mode 100644
index 0000000..0684636
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/QueueControllerTest.java
@@ -0,0 +1,161 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.AbstractConfiguredObject;
+import org.apache.qpid.server.model.AlternateBinding;
+import org.apache.qpid.server.model.Binding;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class QueueControllerTest extends UnitTestBase
+{
+    private LegacyManagementController _legacyVersionManagementController;
+    private QueueController _queueController;
+
+    @Before
+    public void setUp()
+    {
+        _legacyVersionManagementController = mock(LegacyManagementController.class);
+        final ManagementController nextVersionManagementController = mock(ManagementController.class);
+        when(_legacyVersionManagementController.getNextVersionManagementController()).thenReturn(
+                nextVersionManagementController);
+        _queueController = new QueueController(_legacyVersionManagementController, Collections.emptySet());
+    }
+
+    @Test
+    public void convertNextVersionLegacyConfiguredObject()
+    {
+        final String exchangeName = "testExchange";
+        final String alternateExchangeName = "altExchange";
+        final String queueName = "testQueue";
+        final String bindingKey = "testBindingKey";
+
+        final LegacyConfiguredObject nextVersionQueue = mock(LegacyConfiguredObject.class);
+        final Binding nextVersionBinding = mock(Binding.class);
+        final LegacyConfiguredObject nextVersionVirtualHost = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject nextVersionAlternateExchange = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject nextVersionExchange = mock(LegacyConfiguredObject.class);
+
+        final AlternateBinding alternateDestination = mock(AlternateBinding.class);
+        when(alternateDestination.getDestination()).thenReturn(alternateExchangeName);
+
+        when(nextVersionQueue.getCategory()).thenReturn(QueueController.TYPE);
+        when(nextVersionQueue.getParent(VirtualHostController.TYPE)).thenReturn(nextVersionVirtualHost);
+        when(nextVersionQueue.getAttribute("alternateBinding")).thenReturn(alternateDestination);
+        when(nextVersionQueue.getAttribute(AbstractConfiguredObject.NAME)).thenReturn(queueName);
+        when(nextVersionQueue.getAttribute("overflowPolicy")).thenReturn("PRODUCER_FLOW_CONTROL");
+        when(nextVersionQueue.getAttribute("maximumQueueDepthBytes")).thenReturn(10000L);
+        when(nextVersionQueue.getAttribute("context")).thenReturn(Collections.singletonMap("queue.queueFlowResumeLimit", "70"));
+        when(nextVersionQueue.getAttribute("messageGroupType")).thenReturn("SHARED_GROUPS");
+        when(nextVersionQueue.getAttribute("messageGroupKeyOverride")).thenReturn("test");
+
+        when(nextVersionBinding.getDestination()).thenReturn(queueName);
+        when(nextVersionBinding.getBindingKey()).thenReturn(bindingKey);
+
+        when(nextVersionExchange.getAttribute(AbstractConfiguredObject.NAME)).thenReturn(exchangeName);
+        when(nextVersionExchange.getCategory()).thenReturn(ExchangeController.TYPE);
+        when(nextVersionExchange.getAttribute("bindings")).thenReturn(Collections.singletonList(nextVersionBinding));
+
+        when(nextVersionAlternateExchange.getCategory()).thenReturn(ExchangeController.TYPE);
+        when(nextVersionAlternateExchange.getCategory()).thenReturn(ExchangeController.TYPE);
+        when(nextVersionAlternateExchange.getAttribute(LegacyConfiguredObject.NAME)).thenReturn(alternateExchangeName);
+
+        when(nextVersionVirtualHost.getChildren(ExchangeController.TYPE)).thenReturn(Arrays.asList(nextVersionExchange, nextVersionAlternateExchange));
+        when(nextVersionVirtualHost.getChildren(QueueController.TYPE)).thenReturn(Collections.singletonList(nextVersionExchange));
+
+        final LegacyConfiguredObject convertedExchange = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject convertedAltExchange = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject convertedQueue = mock(LegacyConfiguredObject.class);
+        when(_legacyVersionManagementController.convertFromNextVersion(nextVersionQueue)).thenReturn(
+                convertedQueue);
+        when(_legacyVersionManagementController.convertFromNextVersion(nextVersionAlternateExchange)).thenReturn(
+                convertedAltExchange);
+        when(_legacyVersionManagementController.convertFromNextVersion(nextVersionExchange)).thenReturn(convertedExchange);
+
+        final LegacyConfiguredObject destination = _queueController.convertFromNextVersion(nextVersionQueue);
+
+        assertThat(destination.getAttribute("alternateExchange"), is(equalTo(convertedAltExchange)));
+        assertThat(destination.getAttribute("queueFlowControlSizeBytes"), is(equalTo(10000L)));
+        assertThat(destination.getAttribute("queueFlowResumeSizeBytes"), is(equalTo(7000L)));
+        assertThat(destination.getAttribute("messageGroupSharedGroups"), is(equalTo(true)));
+        assertThat(destination.getAttribute("messageGroupKey"), is(equalTo("test")));
+
+        final Collection<LegacyConfiguredObject> children = destination.getChildren(BindingController.TYPE);
+        assertThat(children.size(), is(equalTo(1)));
+
+        final LegacyConfiguredObject o = children.iterator().next();
+        assertThat(o.getCategory(), is(equalTo(BindingController.TYPE)));
+        assertThat(o.getAttribute(AbstractConfiguredObject.NAME), is(equalTo(bindingKey)));
+        assertThat(o.getAttribute("queue"), is(equalTo(convertedQueue)));
+        assertThat(o.getAttribute("exchange"), is(equalTo(convertedExchange)));
+    }
+
+    @Test
+    public void convertAttributesToNextVersion()
+    {
+        final Map<String, Object> attributes = new HashMap<>();
+        attributes.put("queueFlowResumeSizeBytes", 7000L);
+        attributes.put("queueFlowControlSizeBytes", 10000L);
+        attributes.put("messageGroupSharedGroups", true);
+        attributes.put("messageGroupKey", "groupKey");
+        attributes.put("name", "testQueue");
+
+
+        final ConfiguredObject<?> root = mock(ConfiguredObject.class);
+        final List path = Arrays.asList("my-vhn", "my-vh", "testQueue");
+        final Map<String, Object> converted = _queueController.convertAttributesToNextVersion(root, path, attributes);
+
+        assertThat(converted, is(notNullValue()));
+        assertThat(converted.get("overflowPolicy"), is(equalTo("PRODUCER_FLOW_CONTROL")));
+        assertThat(converted.get("maximumQueueDepthBytes"), is(equalTo(10000L)));
+        assertThat(converted.get("messageGroupType"), is(equalTo("SHARED_GROUPS")));
+        assertThat(converted.get("messageGroupKeyOverride"), is(equalTo("groupKey")));
+        assertThat(converted.get("name"), is(equalTo("testQueue")));
+
+        final Object contextObject = converted.get("context");
+        assertThat(contextObject, is(instanceOf(Map.class)));
+
+        final Map<?,?> context =(Map<?,?>)contextObject;
+        assertThat(context.get("queue.queueFlowResumeLimit"), is(equalTo("70.00")));
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/SessionControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/SessionControllerTest.java
new file mode 100644
index 0000000..77b5924
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/SessionControllerTest.java
@@ -0,0 +1,95 @@
+/*
+ *
+ * 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.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.UUID;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class SessionControllerTest extends UnitTestBase
+{
+    private LegacyManagementController _legacyManagementController;
+    private SessionController _sessionController;
+
+    @Before
+    public void setUp()
+    {
+        _legacyManagementController = mock(LegacyManagementController.class);
+        final ManagementController nextVersionManagementController = mock(ManagementController.class);
+        when(_legacyManagementController.getNextVersionManagementController()).thenReturn(
+                nextVersionManagementController);
+        _sessionController = new SessionController(_legacyManagementController, Collections.emptySet());
+    }
+
+    @Test
+    public void convertNextVersionLegacyConfiguredObject()
+    {
+        final UUID sessionID = UUID.randomUUID();
+        final UUID connectionID = UUID.randomUUID();
+
+        final LegacyConfiguredObject nextVersionSession = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject nextVersionConnection = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject nextVersionVirtualHost = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject nextVersionQueue = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject nextVersionConsumer = mock(LegacyConfiguredObject.class);
+
+        when(nextVersionSession.getCategory()).thenReturn(SessionController.TYPE);
+        when(nextVersionSession.getParent(LegacyCategoryControllerFactory.CATEGORY_CONNECTION)).thenReturn(nextVersionConnection);
+        when(nextVersionSession.getAttribute(LegacyConfiguredObject.ID)).thenReturn(sessionID);
+
+        when(nextVersionConnection.getParent(VirtualHostController.TYPE)).thenReturn(nextVersionVirtualHost);
+        when(nextVersionConnection.getAttribute(LegacyConfiguredObject.ID)).thenReturn(connectionID);
+
+        when(nextVersionVirtualHost.getChildren(QueueController.TYPE)).thenReturn(Collections.singletonList(nextVersionQueue));
+        when(nextVersionQueue.getChildren(ConsumerController.TYPE)).thenReturn(Collections.singletonList(nextVersionConsumer));
+        when(nextVersionConsumer.getAttribute("session")).thenReturn(nextVersionSession);
+
+        final LegacyConfiguredObject convertedConsumer = mock(LegacyConfiguredObject.class);
+        final LegacyConfiguredObject convertedConnection = mock(LegacyConfiguredObject.class);
+        when(_legacyManagementController.convertFromNextVersion(nextVersionConsumer)).thenReturn(convertedConsumer);
+        when(_legacyManagementController.convertFromNextVersion(nextVersionConnection)).thenReturn(convertedConnection);
+
+        final LegacyConfiguredObject convertedSession = _sessionController.convertNextVersionLegacyConfiguredObject(nextVersionSession);
+
+        assertThat(convertedSession.getAttribute(LegacyConfiguredObject.ID), is(equalTo(sessionID)));
+        assertThat(convertedSession.getParent(LegacyCategoryControllerFactory.CATEGORY_CONNECTION), is(equalTo(convertedConnection)));
+
+        final Collection<LegacyConfiguredObject> consumers = convertedSession.getChildren(ConsumerController.TYPE);
+        assertThat(consumers, is(notNullValue()));
+        assertThat(consumers.size(), is(equalTo(1)));
+        assertThat(consumers.iterator().next(), is(equalTo(convertedConsumer)));
+    }
+}
\ No newline at end of file
diff --git a/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/VirtualHostControllerTest.java b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/VirtualHostControllerTest.java
new file mode 100644
index 0000000..8b37137
--- /dev/null
+++ b/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/controller/v6_1/category/VirtualHostControllerTest.java
@@ -0,0 +1,104 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.management.plugin.controller.v6_1.category;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.ManagementController;
+import org.apache.qpid.server.management.plugin.controller.LegacyConfiguredObject;
+import org.apache.qpid.server.management.plugin.controller.LegacyManagementController;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class VirtualHostControllerTest extends UnitTestBase
+{
+    private VirtualHostController _virtualHostController;
+
+    @Before
+    public void setUp()
+    {
+        final LegacyManagementController legacyManagementController = mock(LegacyManagementController.class);
+        final ManagementController nextVersionManagementController = mock(ManagementController.class);
+        when(legacyManagementController.getNextVersionManagementController()).thenReturn(
+                nextVersionManagementController);
+        when(legacyManagementController.getVersion()).thenReturn("6.1");
+        _virtualHostController = new VirtualHostController(legacyManagementController, Collections.emptySet());
+    }
+
+    @Test
+    public void convertNextVersionLegacyConfiguredObject()
+    {
+        final LegacyConfiguredObject nextVersionVirtualHost = mock(LegacyConfiguredObject.class);
+        when(nextVersionVirtualHost.getAttribute(LegacyConfiguredObject.NAME)).thenReturn("test");
+
+        final LegacyConfiguredObject converted =
+                _virtualHostController.convertNextVersionLegacyConfiguredObject(nextVersionVirtualHost);
+
+        assertThat(converted, is(notNullValue()));
+        assertThat(converted.getCategory(), is(equalTo(VirtualHostController.TYPE)));
+        assertThat(converted.getAttribute(LegacyConfiguredObject.NAME), is(equalTo("test")));
+        assertThat(converted.getAttribute("modelVersion"), is(equalTo("6.1")));
+        assertThat(converted.getAttribute("queue_deadLetterQueueEnabled"), is(equalTo(false)));
+    }
+
+    @Test
+    public void convertAttributesToNextVersion()
+    {
+        Map<String, Object> attributes = new HashMap<>();
+        attributes.put(LegacyConfiguredObject.NAME, "test");
+        attributes.put("queue_deadLetterQueueEnabled", true);
+        Map<String, String> context = new HashMap<>();
+        context.put("queue.deadLetterQueueEnabled", "true");
+        context.put("virtualhost.housekeepingCheckPeriod", "30");
+        attributes.put(LegacyConfiguredObject.CONTEXT, context);
+
+        final ConfiguredObject<?> root = mock(ConfiguredObject.class);
+        final List<String> path = Collections.singletonList("test-vhn");
+        Map<String, Object> converted = _virtualHostController.convertAttributesToNextVersion(root, path, attributes);
+
+        assertThat(converted, is(notNullValue()));
+        assertThat(converted.get(LegacyConfiguredObject.NAME), is(equalTo("test")));
+
+        assertThat(converted.containsKey("queue_deadLetterQueueEnabled"), is(equalTo(false)));
+
+        Object contextObject = converted.get(LegacyConfiguredObject.CONTEXT);
+        assertThat(converted, is(instanceOf(Map.class)));
+
+        Map<?,?> convertedContext = (Map<?,?>)contextObject;
+
+        assertThat(convertedContext.get("virtualhost.housekeepingCheckPeriod"), is(equalTo("30")));
+        assertThat(convertedContext.containsKey("queue.deadLetterQueueEnabled"), is(equalTo(false)));
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org