ARIES-2030 wip

Signed-off-by: Raymond Auge <rotty3000@apache.org>
diff --git a/integrations/openapi/openapi-resource/bnd.bnd b/integrations/openapi/openapi-resource/bnd.bnd
index 13ef2e7..f059bd7 100644
--- a/integrations/openapi/openapi-resource/bnd.bnd
+++ b/integrations/openapi/openapi-resource/bnd.bnd
@@ -15,6 +15,6 @@
 #    specific language governing permissions and limitations
 #    under the License.
 
-Export-Package: org.apache.aries.jaxrs.openapi;version=${openapi.version}
+Export-Package: org.apache.aries.jax.rs.openapi;version=${openapi.version}
 
 -conditionalpackage: org.apache.aries.component.dsl.*
\ No newline at end of file
diff --git a/integrations/openapi/openapi-resource/pom.xml b/integrations/openapi/openapi-resource/pom.xml
index 07509c3..405471f 100644
--- a/integrations/openapi/openapi-resource/pom.xml
+++ b/integrations/openapi/openapi-resource/pom.xml
@@ -64,6 +64,10 @@
                 </exclusion>
             </exclusions>
         </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.namespace.implementation</artifactId>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/integrations/openapi/openapi-resource/src/main/java/org/apache/aries/jax/rs/openapi/OpenApiResource.java b/integrations/openapi/openapi-resource/src/main/java/org/apache/aries/jax/rs/openapi/OpenApiResource.java
index a4cc6c3..03d1828 100644
--- a/integrations/openapi/openapi-resource/src/main/java/org/apache/aries/jax/rs/openapi/OpenApiResource.java
+++ b/integrations/openapi/openapi-resource/src/main/java/org/apache/aries/jax/rs/openapi/OpenApiResource.java
@@ -11,6 +11,8 @@
 import javax.ws.rs.ext.Providers;
 
 import org.apache.aries.jax.rs.whiteboard.ApplicationClasses;
+import org.osgi.annotation.bundle.Capability;
+import org.osgi.namespace.implementation.ImplementationNamespace;
 
 import io.swagger.v3.core.util.Json;
 import io.swagger.v3.core.util.Yaml;
@@ -21,6 +23,10 @@
 import io.swagger.v3.oas.integration.api.OpenApiContext;
 import io.swagger.v3.oas.models.OpenAPI;
 
+@Capability(
+    namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE,
+    name = "org.apache.aries.jax.rs.openapi"
+)
 @Path("/openapi.{type:json|yaml}")
 public class OpenApiResource extends BaseOpenApiResource {
 
diff --git a/integrations/pom.xml b/integrations/pom.xml
index 8fd08cb..9d877e9 100644
--- a/integrations/pom.xml
+++ b/integrations/pom.xml
@@ -48,6 +48,7 @@
         <module>jackson</module>
         <module>cxf-jettison</module>
         <module>openapi</module>
+        <module>rest-management</module>
     </modules>
 
 </project>
diff --git a/integrations/rest-management/pom.xml b/integrations/rest-management/pom.xml
new file mode 100644
index 0000000..7eec35c
--- /dev/null
+++ b/integrations/rest-management/pom.xml
@@ -0,0 +1,44 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.aries.jax.rs</groupId>
+        <artifactId>integration</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>rest-management-integration</artifactId>
+    <name>Apache Aries JAX-RS OSGi REST Management Integration Project</name>
+    <packaging>pom</packaging>
+
+    <scm>
+        <connection>scm:git:http://gitbox.apache.org/repos/asf/aries-jax-rs-whiteboard.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/aries-jax-rs-whiteboard.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=aries-jax-rs-whiteboard.git</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <modules>
+        <module>rest-management</module>
+        <module>rest-management-itest</module>
+    </modules>
+
+</project>
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management-itest/LICENSE b/integrations/rest-management/rest-management-itest/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/integrations/rest-management/rest-management-itest/bnd.bnd b/integrations/rest-management/rest-management-itest/bnd.bnd
new file mode 100644
index 0000000..eb946df
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/bnd.bnd
@@ -0,0 +1,20 @@
+#    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.
+
+Bundle-Description: Integration Test bundle for the REST Management integration
+
+Test-Cases: ${classes;CONCRETE;ANNOTATED;org.junit.Test}
diff --git a/integrations/rest-management/rest-management-itest/itest.bndrun b/integrations/rest-management/rest-management-itest/itest.bndrun
new file mode 100644
index 0000000..91be3f9
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/itest.bndrun
@@ -0,0 +1,120 @@
+#    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.
+
+-runpath: \
+    ch.qos.logback.classic,\
+    ch.qos.logback.core,\
+    org.apache.felix.logback,\
+    slf4j.api
+-runsystempackages: \
+    org.slf4j;version=${slf4j.version},\
+    org.slf4j.event;version=${slf4j.version},\
+    org.slf4j.helpers;version=${slf4j.version},\
+    org.slf4j.spi;version=${slf4j.version},\
+    com.sun.net.httpserver
+
+-runfw: org.eclipse.osgi
+
+#-runjdb: 8000
+#-runtrace: true
+
+-resolve.effective: resolve, active
+
+-runrequires: \
+    osgi.identity;filter:='(osgi.identity=org.apache.aries.jax.rs.rest.management)',\
+    osgi.identity;filter:='(osgi.identity=org.apache.aries.jax.rs.rest.management.itest)',\
+    osgi.identity;filter:='(osgi.identity=org.apache.felix.gogo.command)',\
+    osgi.identity;filter:='(osgi.identity=org.apache.johnzon.mapper)'
+
+-runsystemcapabilities: ${native_capability}
+-runproperties: \
+    logback.configurationFile=${fileuri;${.}/logback.xml},\
+    osgi.console=, \
+    org.apache.cxf.osgi.http.transport.disable=true,\
+    org.apache.felix.http.host=localhost,\
+    org.osgi.service.http.port=*,\
+    org.apache.aries.spifly.auto.consumers=jakarta.*,\
+    org.apache.aries.spifly.auto.providers=com.sun.*
+
+-runbundles: \
+	assertj-core;version='[3.19.0,3.19.1)',\
+	com.fasterxml.jackson.core.jackson-annotations;version='[2.10.1,2.10.2)',\
+	com.fasterxml.jackson.core.jackson-core;version='[2.10.1,2.10.2)',\
+	com.fasterxml.jackson.core.jackson-databind;version='[2.10.1,2.10.2)',\
+	com.fasterxml.jackson.dataformat.jackson-dataformat-yaml;version='[2.10.1,2.10.2)',\
+	com.fasterxml.jackson.datatype.jackson-datatype-jsr310;version='[2.10.1,2.10.2)',\
+	com.fasterxml.woodstox.woodstox-core;version='[6.2.3,6.2.4)',\
+	com.jayway.jsonpath.json-path;version='[2.4.0,2.4.1)',\
+	com.sun.istack.commons-runtime;version='[3.0.11,3.0.12)',\
+	com.sun.xml.bind.jaxb-osgi;version='[2.3.3,2.3.4)',\
+	io.github.classgraph.classgraph;version='[4.6.32,4.6.33)',\
+	io.swagger.core.v3.swagger-annotations;version='[2.1.0,2.1.1)',\
+	io.swagger.core.v3.swagger-core;version='[2.1.0,2.1.1)',\
+	io.swagger.core.v3.swagger-integration;version='[2.1.0,2.1.1)',\
+	io.swagger.core.v3.swagger-jaxrs2;version='[2.1.0,2.1.1)',\
+	io.swagger.core.v3.swagger-models;version='[2.1.0,2.1.1)',\
+	jakarta.xml.bind-api;version='[2.3.3,2.3.4)',\
+	javax.validation.api;version='[1.1.0,1.1.1)',\
+	net.javacrumbs.json-unit.json-unit-assertj;version='[2.24.0,2.24.1)',\
+	net.javacrumbs.json-unit.json-unit-core;version='[2.24.0,2.24.1)',\
+	net.javacrumbs.json-unit.json-unit-json-path;version='[2.24.0,2.24.1)',\
+	net.minidev.accessors-smart;version='[1.2.0,1.2.1)',\
+	net.minidev.json-smart;version='[2.3.0,2.3.1)',\
+	org.apache.aries.jax.rs.openapi.resource;version='[2.0.0,2.0.1)',\
+	org.apache.aries.jax.rs.rest.management;version='[2.0.0,2.0.1)',\
+	org.apache.aries.jax.rs.rest.management.itest;version='[2.0.0,2.0.1)',\
+	org.apache.aries.jax.rs.whiteboard;version='[2.0.0,2.0.1)',\
+	org.apache.aries.spifly.dynamic.framework.extension;version='[1.3.2,1.3.3)',\
+	org.apache.commons.lang3;version='[3.7.0,3.7.1)',\
+	org.apache.cxf.cxf-core;version='[3.4.2,3.4.3)',\
+	org.apache.cxf.cxf-rt-frontend-jaxrs;version='[3.4.2,3.4.3)',\
+	org.apache.cxf.cxf-rt-rs-client;version='[3.4.2,3.4.3)',\
+	org.apache.cxf.cxf-rt-rs-sse;version='[3.4.2,3.4.3)',\
+	org.apache.cxf.cxf-rt-security;version='[3.4.2,3.4.3)',\
+	org.apache.cxf.cxf-rt-transports-http;version='[3.4.2,3.4.3)',\
+	org.apache.felix.configadmin;version='[1.9.20,1.9.21)',\
+	org.apache.felix.gogo.command;version='[1.1.2,1.1.3)',\
+	org.apache.felix.gogo.runtime;version='[1.1.4,1.1.5)',\
+	org.apache.felix.gogo.shell;version='[1.1.4,1.1.5)',\
+	org.apache.felix.http.jetty;version='[4.1.4,4.1.5)',\
+	org.apache.felix.http.servlet-api;version='[1.1.2,1.1.3)',\
+	org.apache.geronimo.specs.geronimo-annotation_1.3_spec;version='[1.3.0,1.3.1)',\
+	org.apache.geronimo.specs.geronimo-jaxrs_2.1_spec;version='[1.1.0,1.1.1)',\
+	org.apache.geronimo.specs.geronimo-json_1.1_spec;version='[1.5.0,1.5.1)',\
+	org.apache.johnzon.core;version='[1.2.10,1.2.11)',\
+	org.apache.johnzon.mapper;version='[1.2.10,1.2.11)',\
+	org.apache.servicemix.bundles.junit;version='[4.13.0,4.13.1)',\
+	org.apache.ws.xmlschema.core;version='[2.2.5,2.2.6)',\
+	org.hamcrest;version='[2.2.0,2.2.1)',\
+	org.objectweb.asm;version='[5.0.4,5.0.5)',\
+	org.osgi.service.jaxrs;version='[1.0.0,1.0.1)',\
+	org.osgi.service.rest;version='[1.0.0,1.0.1)',\
+	org.osgi.test.assertj.framework;version='[0.10.0,0.10.1)',\
+	org.osgi.test.common;version='[0.10.0,0.10.1)',\
+	org.osgi.test.junit4;version='[0.10.0,0.10.1)',\
+	org.osgi.util.function;version='[1.1.0,1.1.1)',\
+	org.osgi.util.promise;version='[1.1.1,1.1.2)',\
+	org.xmlunit.xmlunit-assertj3;version='[2.8.2,2.8.3)',\
+	org.xmlunit.xmlunit-core;version='[2.8.2,2.8.3)',\
+	org.yaml.snakeyaml;version='[1.24.0,1.24.1)',\
+	stax2-api;version='[4.2.1,4.2.2)'
+
+-runstartlevel: \
+    order=sortbynameversion,\
+    begin=-1
+
+-include: -personal.bnd
diff --git a/integrations/rest-management/rest-management-itest/logback.xml b/integrations/rest-management/rest-management-itest/logback.xml
new file mode 100644
index 0000000..b3a37f9
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/logback.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<configuration>
+  <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
+    <resetJUL>true</resetJUL>
+  </contextListener>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="error">
+    <appender-ref ref="STDOUT" />
+  </root>
+</configuration>
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management-itest/pom.xml b/integrations/rest-management/rest-management-itest/pom.xml
new file mode 100644
index 0000000..4a324c7
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/pom.xml
@@ -0,0 +1,118 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.aries.jax.rs</groupId>
+        <artifactId>rest-management-integration</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>org.apache.aries.jax.rs.rest.management.itest</artifactId>
+    <name>Apache Aries JAX-RS OSGi REST Management integration tests</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jax.rs</groupId>
+            <artifactId>org.apache.aries.jax.rs.whiteboard</artifactId>
+            <version>${project.version}</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jax.rs</groupId>
+            <artifactId>org.apache.aries.jax.rs.rest.management</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.component-dsl</groupId>
+            <artifactId>org.apache.aries.component-dsl.component-dsl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.rest</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.enroute.junit.wrapper</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.enroute.hamcrest.wrapper</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.test.assertj.framework</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.test.junit4</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit-assertj</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.xmlunit</groupId>
+            <artifactId>xmlunit-assertj3</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-resolver-maven-plugin</artifactId>
+                <configuration>
+                    <failOnChanges>false</failOnChanges>
+                    <includeDependencyManagement>true</includeDependencyManagement>
+                    <bndruns>
+                        <bndrun>itest.bndrun</bndrun>
+                    </bndruns>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-testing-maven-plugin</artifactId>
+                <configuration>
+                    <failOnChanges>true</failOnChanges>
+                    <resolve>false</resolve>
+                    <includeDependencyManagement>true</includeDependencyManagement>
+                    <bndruns>
+                        <bndrun>itest.bndrun</bndrun>
+                    </bndruns>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleHeaderTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleHeaderTest.java
new file mode 100644
index 0000000..fc54384
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleHeaderTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLEHEADER_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLEHEADER_XML_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Dictionary;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.test.common.annotation.InjectBundleContext;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class BundleHeaderTest extends TestUtil {
+
+    @Rule
+    public BundleContextRule bundleContextRule = new BundleContextRule();
+
+    @InjectBundleContext
+    BundleContext bundleContext;
+
+    @Test
+    public void getBundleHeaderJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}").path("header");
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(
+            APPLICATION_BUNDLEHEADER_JSON_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("Bnd-LastModified").isString()
+        );
+    }
+
+    @Test
+    public void getBundleHeaderXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}").path("header");
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(
+            APPLICATION_BUNDLEHEADER_XML_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid(
+        ).valueByXPath(
+            "//bundleHeader/entry/@key"
+        ).isEqualTo(
+            "Bundle-Name"
+        );
+    }
+
+    @Test
+    public void getBundleHeaderDictionaryJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}").path("header");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(
+            APPLICATION_BUNDLEHEADER_JSON_TYPE
+        ).get();
+
+        Dictionary<String, String> bundleHeader = response.readEntity(
+            new GenericType<Dictionary<String, String>>() {});
+
+        assertThat(
+            bundleHeader
+        ).hasFieldOrPropertyWithValue(
+            "Manifest-Version", "1.0"
+        ).hasFieldOrPropertyWithValue(
+            "Bundle-ManifestVersion", "2"
+        );
+    }
+
+    @Test
+    public void getBundleHeaderDictionaryXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}").path("header");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(
+            APPLICATION_BUNDLEHEADER_XML_TYPE
+        ).get();
+
+        Dictionary<String, String> bundleHeader = response.readEntity(
+            new GenericType<Dictionary<String, String>>() {});
+
+        assertThat(
+            bundleHeader
+        ).hasFieldOrPropertyWithValue(
+            "Manifest-Version", "1.0"
+        ).hasFieldOrPropertyWithValue(
+            "Bundle-ManifestVersion", "2"
+        );
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleStartLevelTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleStartLevelTest.java
new file mode 100644
index 0000000..4852bbe
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleStartLevelTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTARTLEVEL_XML_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.junit.Test;
+import org.osgi.framework.startlevel.dto.BundleStartLevelDTO;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class BundleStartLevelTest extends TestUtil {
+
+    @Test
+    public void getBundleStartLevelJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundle").path("{bundleid}").path("startlevel");
+
+        Response response = target.resolveTemplate("bundleid", 2l).request().get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("startLevel").isNumber().isEqualTo("1"),
+            j -> j.node("activationPolicyUsed").isBoolean().isTrue(),
+            j -> j.node("persistentlyStarted").isBoolean().isTrue()
+        );
+    }
+
+    @Test
+    public void getBundleStartLevelJSONExplicit() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundle").path("{bundleid}").path("startlevel");
+
+        Response response = target.resolveTemplate("bundleid", 2l).request(
+            APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("startLevel").isNumber().isEqualTo("1"),
+            j -> j.node("activationPolicyUsed").isBoolean().isTrue(),
+            j -> j.node("persistentlyStarted").isBoolean().isTrue()
+        );
+    }
+
+    @Test
+    public void getBundleStartLevelXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundle").path("{bundleid}").path("startlevel");
+
+        Response response = target.resolveTemplate("bundleid", 2l).request(
+            APPLICATION_BUNDLESTARTLEVEL_XML_TYPE).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid(
+        ).valueByXPath(
+            "//bundleStartLevel/bundle"
+        ).isEqualTo("2");
+    }
+
+    @Test
+    public void getBundleStartLevelDTO() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundle").path("{bundleid}").path("startlevel");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.resolveTemplate("bundleid", 2l).request().get();
+
+        BundleStartLevelDTO bundleStartLevelDTO =
+            response.readEntity(BundleStartLevelDTO.class);
+
+        assertThat(
+            bundleStartLevelDTO.startLevel
+        ).isGreaterThan(
+            0
+        );
+    }
+
+    @Test
+    public void putBundleStartLevelBadValues() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundle").path("{bundleid}").path("startlevel");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        BundleStartLevelDTO bundleStartLevelDTO =
+            new BundleStartLevelDTO();
+
+        Response response = target.resolveTemplate("bundleid", 2l).request().put(
+            Entity.entity(
+                bundleStartLevelDTO, APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE));
+
+        assertThat(
+            response.getStatus()
+        ).isEqualTo(
+            400
+        );
+    }
+
+    @Test
+    public void putBundleStartLevel() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundle").path("{bundleid}").path("startlevel");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        BundleStartLevelDTO bundleStartLevelDTO =
+            new BundleStartLevelDTO();
+
+        bundleStartLevelDTO.startLevel = 2;
+
+        try {
+            Response response = target.resolveTemplate("bundleid", 2l).request().put(
+                Entity.entity(
+                    bundleStartLevelDTO, APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE));
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+        }
+        finally {
+            bundleStartLevelDTO.startLevel = 1;
+
+            Response response = target.resolveTemplate("bundleid", 2l).request().put(
+                Entity.entity(
+                    bundleStartLevelDTO, APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE));
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+        }
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleStateTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleStateTest.java
new file mode 100644
index 0000000..39df806
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleStateTest.java
@@ -0,0 +1,356 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTATE_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTATE_XML_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.RestManagementConstants;
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.apache.aries.jax.rs.rest.management.model.BundleStateDTO;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.test.common.annotation.InjectBundleContext;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class BundleStateTest extends TestUtil {
+
+    @Rule
+    public BundleContextRule bundleContextRule = new BundleContextRule();
+
+    @InjectBundleContext
+    BundleContext bundleContext;
+
+    @Test
+    public void getBundleStateJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}").path("state");
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(
+            APPLICATION_BUNDLESTATE_JSON_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("state").isNumber().isEqualTo("32"),
+            j -> j.node("options").isNumber().isEqualTo("2")
+        );
+    }
+
+    @Test
+    public void getBundleStateXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}").path("state");
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(
+            APPLICATION_BUNDLESTATE_XML_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid(
+        ).valueByXPath(
+            "//bundleState/state"
+        ).isEqualTo("32");
+   }
+
+    @Test
+    public void getBundleStateDTO() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}").path("state");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(
+            APPLICATION_BUNDLESTATE_JSON_TYPE
+        ).get();
+
+        BundleStateDTO bundleStateDTO = response.readEntity(BundleStateDTO.class);
+
+        assertThat(
+            bundleStateDTO
+        ).hasFieldOrPropertyWithValue(
+            "state", 32
+        );
+    }
+
+    @Test
+    public void putBundleState_start() throws Exception {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundles");
+
+        BundleDTO bundleDTO = null;
+
+        try {
+            Response response = target.request().post(
+                Entity.entity(
+                    BundleStateTest.class.getClassLoader().getResourceAsStream("minor-change-1.0.1.jar"),
+                    RestManagementConstants.APPLICATION_VNDOSGIBUNDLE_TYPE)
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleDTO = response.readEntity(BundleDTO.class);
+
+            assertThat(
+                bundleDTO.symbolicName
+            ).isEqualTo(
+                "minor-and-removed-change"
+            );
+            assertThat(
+                bundleDTO.state
+            ).isEqualTo(
+                2
+            );
+
+            target = createDefaultTarget().path(
+                "framework").path("bundle").path("{bundleid}").path("state");
+
+            BundleStateDTO bundleStateDTO = new BundleStateDTO();
+            bundleStateDTO.state = Bundle.ACTIVE;
+
+            response = target.resolveTemplate(
+                "bundleid", bundleDTO.id
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleStateDTO = response.readEntity(BundleStateDTO.class);
+
+            assertThat(
+                bundleStateDTO.state
+            ).isEqualTo(
+                32
+            );
+        }
+        finally {
+            if (bundleDTO != null) {
+                bundleContext.getBundle(bundleDTO.id).uninstall();
+            }
+        }
+    }
+
+    @Test
+    public void putBundleState_resolve() throws Exception {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundles");
+
+        BundleDTO bundleDTO = null;
+
+        try {
+            Response response = target.request().post(
+                Entity.entity(
+                    BundleStateTest.class.getClassLoader().getResourceAsStream("minor-change-1.0.1.jar"),
+                    RestManagementConstants.APPLICATION_VNDOSGIBUNDLE_TYPE)
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleDTO = response.readEntity(BundleDTO.class);
+
+            assertThat(
+                bundleDTO.symbolicName
+            ).isEqualTo(
+                "minor-and-removed-change"
+            );
+            assertThat(
+                bundleDTO.state
+            ).isEqualTo(
+                2
+            );
+
+            target = createDefaultTarget().path(
+                "framework").path("bundle").path("{bundleid}").path("state");
+
+            BundleStateDTO bundleStateDTO = new BundleStateDTO();
+            bundleStateDTO.state = Bundle.RESOLVED;
+
+            response = target.resolveTemplate(
+                "bundleid", bundleDTO.id
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleStateDTO = response.readEntity(BundleStateDTO.class);
+
+            assertThat(
+                bundleStateDTO.state
+            ).isEqualTo(
+                Bundle.RESOLVED
+            );
+        }
+        finally {
+            if (bundleDTO != null) {
+                bundleContext.getBundle(bundleDTO.id).uninstall();
+            }
+        }
+    }
+
+    @Test
+    public void putBundleState_stop() throws Exception {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundles");
+
+        BundleDTO bundleDTO = null;
+
+        try {
+            Response response = target.request().post(
+                Entity.entity(
+                    BundleStateTest.class.getClassLoader().getResourceAsStream("minor-change-1.0.1.jar"),
+                    RestManagementConstants.APPLICATION_VNDOSGIBUNDLE_TYPE)
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleDTO = response.readEntity(BundleDTO.class);
+
+            assertThat(
+                bundleDTO.symbolicName
+            ).isEqualTo(
+                "minor-and-removed-change"
+            );
+            assertThat(
+                bundleDTO.state
+            ).isEqualTo(
+                2
+            );
+
+            target = createDefaultTarget().path(
+                "framework").path("bundle").path("{bundleid}").path("state");
+
+            BundleStateDTO bundleStateDTO = new BundleStateDTO();
+            bundleStateDTO.state = Bundle.ACTIVE;
+
+            response = target.resolveTemplate(
+                "bundleid", bundleDTO.id
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleStateDTO = response.readEntity(BundleStateDTO.class);
+
+            assertThat(
+                bundleStateDTO.state
+            ).isEqualTo(
+                32
+            );
+
+            ////////////////
+
+            target = createDefaultTarget().path(
+                "framework").path("bundle").path("{bundleid}").path("state");
+
+            bundleStateDTO = new BundleStateDTO();
+            bundleStateDTO.state = Bundle.RESOLVED;
+
+            response = target.resolveTemplate(
+                "bundleid", bundleDTO.id
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleStateDTO = response.readEntity(BundleStateDTO.class);
+
+            assertThat(
+                bundleStateDTO.state
+            ).isEqualTo(
+                Bundle.RESOLVED
+            );
+        }
+        finally {
+            if (bundleDTO != null) {
+                bundleContext.getBundle(bundleDTO.id).uninstall();
+            }
+        }
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleTest.java
new file mode 100644
index 0000000..0f58563
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundleTest.java
@@ -0,0 +1,304 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLE_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLE_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_VNDOSGIBUNDLE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.osgi.framework.Bundle.UNINSTALLED;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.test.common.annotation.InjectBundleContext;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class BundleTest extends TestUtil {
+
+    @Rule
+    public BundleContextRule bundleContextRule = new BundleContextRule();
+
+    @InjectBundleContext
+    BundleContext bundleContext;
+
+    @Test
+    public void getBundleJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}");
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(APPLICATION_BUNDLE_JSON_TYPE).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("id").isNumber().isEqualTo("2"),
+            j -> j.node("lastModified").isNumber(),
+            j -> j.node("state").isNumber().isEqualByComparingTo("32"),
+            j -> j.node("symbolicName").isString().isEqualTo("com.fasterxml.jackson.core.jackson-annotations"),
+            j -> j.node("version").isString()
+        );
+    }
+
+    @Test
+    public void getBundleXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}");
+
+        Response response = target.resolveTemplate("bundleid", 2l).request(
+            APPLICATION_BUNDLE_XML_TYPE).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//bundle/id"
+        ).contains("2");
+   }
+
+    @Test
+    public void getBundleDTO() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.resolveTemplate(
+            "bundleid", 2l
+        ).request(APPLICATION_BUNDLE_JSON_TYPE).get();
+
+        BundleDTO bundleDTO = response.readEntity(BundleDTO.class);
+
+        assertThat(
+            bundleDTO.id
+        ).isEqualTo(2);
+    }
+
+    @Test
+    public void putBundleByLocation() throws Exception {
+        try (HttpServer server = new HttpServer(
+                BundleTest.class.getClassLoader().getResource("minor-change-1.0.1.jar"),
+                "application/zip")) {
+
+            WebTarget target = createDefaultTarget().path(
+                "framework").path("bundles");
+
+            target.register(RestManagementMessageBodyHandler.class);
+
+            BundleDTO bundleDTO = null;
+
+            try {
+                Response response = target.request().post(
+                    Entity.entity(String.format(
+                        "http://localhost:%d/minor-change-1.0.1.jar",
+                        server.getPort()),
+                    MediaType.TEXT_PLAIN));
+
+                assertThat(
+                    response.getStatus()
+                ).isEqualTo(
+                    200
+                );
+
+                bundleDTO = response.readEntity(BundleDTO.class);
+
+                assertThat(
+                    bundleDTO.symbolicName
+                ).isEqualTo(
+                    "minor-and-removed-change"
+                );
+
+                target = createDefaultTarget().path(
+                    "framework").path("bundle").path("{bundleid}");
+
+                target.register(RestManagementMessageBodyHandler.class);
+
+                response = target.resolveTemplate(
+                    "bundleid", bundleDTO.id
+                ).request().put(
+                    Entity.entity(String.format(
+                        "http://localhost:%d/minor-change-1.0.1.jar",
+                        server.getPort()),
+                    MediaType.TEXT_PLAIN));
+
+                assertThat(
+                    response.getStatus()
+                ).isEqualTo(
+                    200
+                );
+
+                bundleDTO = response.readEntity(BundleDTO.class);
+
+                assertThat(
+                    bundleDTO.symbolicName
+                ).isEqualTo(
+                    "minor-and-removed-change"
+                );
+            }
+            finally {
+                if (bundleDTO != null) {
+                    bundleContext.getBundle(bundleDTO.id).uninstall();
+                }
+            }
+        }
+    }
+
+    @Test
+    public void putBundleByUpload() throws Exception {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundles");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        BundleDTO bundleDTO = null;
+
+        try {
+            Response response = target.request().header(
+                HttpHeaders.CONTENT_LOCATION, "minor-change-1.0.1.jar"
+            ).post(
+                Entity.entity(
+                    BundleTest.class.getClassLoader().getResourceAsStream("minor-change-1.0.1.jar"),
+                    APPLICATION_VNDOSGIBUNDLE)
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleDTO = response.readEntity(BundleDTO.class);
+
+            assertThat(
+                bundleDTO.symbolicName
+            ).isEqualTo(
+                "minor-and-removed-change"
+            );
+
+            target = createDefaultTarget().path(
+                "framework"
+            ).path("bundle").path("{bundleid}");
+
+            target.register(RestManagementMessageBodyHandler.class);
+
+            response = target.resolveTemplate(
+                "bundleid", bundleDTO.id
+            ).request().put(
+                Entity.entity(
+                    BundleTest.class.getClassLoader().getResourceAsStream("minor-change-1.0.1.jar"),
+                    APPLICATION_VNDOSGIBUNDLE)
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleDTO = response.readEntity(BundleDTO.class);
+
+            assertThat(
+                bundleDTO.symbolicName
+            ).isEqualTo(
+                "minor-and-removed-change"
+            );
+        }
+        finally {
+            if (bundleDTO != null) {
+                bundleContext.getBundle(bundleDTO.id).uninstall();
+            }
+        }
+    }
+
+    @Test
+    public void deleteBundle() throws Exception {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundles");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        BundleDTO bundleDTO = null;
+
+        Response response = target.request().header(
+            HttpHeaders.CONTENT_LOCATION, "minor-change-1.0.1.jar"
+        ).post(
+            Entity.entity(
+                BundleTest.class.getClassLoader().getResourceAsStream("minor-change-1.0.1.jar"),
+                APPLICATION_VNDOSGIBUNDLE)
+        );
+
+        assertThat(
+            response.getStatus()
+        ).isEqualTo(
+            200
+        );
+
+        bundleDTO = response.readEntity(BundleDTO.class);
+
+        assertThat(
+            bundleDTO.symbolicName
+        ).isEqualTo(
+            "minor-and-removed-change"
+        );
+
+        target = createDefaultTarget().path(
+            "framework"
+        ).path("bundle").path("{bundleid}");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        response = target.resolveTemplate(
+            "bundleid", bundleDTO.id
+        ).request().delete();
+
+        assertThat(
+            response.getStatus()
+        ).isEqualTo(
+            200
+        );
+
+        bundleDTO = response.readEntity(BundleDTO.class);
+
+        assertThat(
+            bundleDTO.state
+        ).matches(
+            state -> (state & UNINSTALLED) == UNINSTALLED
+        );
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundlesTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundlesTest.java
new file mode 100644
index 0000000..1b50680
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/BundlesTest.java
@@ -0,0 +1,484 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_REPRESENTATIONS_JSON;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_REPRESENTATIONS_XML;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_REPRESENTATIONS_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_VNDOSGIBUNDLE_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.RestManagementConstants;
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.apache.aries.jax.rs.rest.management.model.BundlesDTO;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.test.assertj.bundle.BundleAssert;
+import org.osgi.test.common.annotation.InjectBundleContext;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class BundlesTest extends TestUtil {
+
+    @Rule
+    public BundleContextRule bundleContextRule = new BundleContextRule();
+
+    @InjectBundleContext
+    BundleContext bundleContext;
+
+    @Test
+    public void getBundlesJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles");
+
+        Response response = target.request(
+            APPLICATION_BUNDLES_JSON_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node(
+                "bundles"
+            ).isArray(
+            ).element(
+                0
+            ).asString(
+            ).contains(
+                "http://", "/framework/bundle/0"
+            )
+        );
+    }
+
+    @Ignore
+    @Test
+    public void getBundlesJSON_DOT() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles.json");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("bundles").isArray().element(0).asString().contains(
+                "http://", "/framework/bundle/0"
+            )
+        );
+    }
+
+    @Test
+    public void getBundlesXML() {
+        WebTarget target = createDefaultTarget().path("framework").path("bundles");
+
+        Response response = target.request(
+            RestManagementConstants.APPLICATION_BUNDLES_XML_TYPE).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//bundles/uri[1]"
+        ).contains("/framework/bundle/0");
+    }
+
+    @Ignore
+    @Test
+    public void getBundlesXML_DOT() {
+        WebTarget target = createDefaultTarget().path("framework").path("bundles.xml");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//bundles/uri[2]"
+        ).contains("/framework/bundle/1");
+   }
+
+    @Test
+    public void getBundlesDTOJSON() {
+        WebTarget target = createDefaultTarget().path("framework").path("bundles");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request().get();
+
+        BundlesDTO bundlesDTO = response.readEntity(BundlesDTO.class);
+
+        assertTrue(bundlesDTO.toString(), bundlesDTO.bundles.size() > 0);
+    }
+
+    @Test
+    public void getBundlesDTOXML() {
+        WebTarget target = createDefaultTarget().path("framework").path("bundles");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request(
+            APPLICATION_BUNDLES_XML_TYPE
+        ).get();
+
+        BundlesDTO bundlesDTO = response.readEntity(BundlesDTO.class);
+
+        assertTrue(bundlesDTO.toString(), bundlesDTO.bundles.size() > 0);
+    }
+
+    @Test
+    public void getBundleDTOsJSON() {
+        WebTarget target = createDefaultTarget().path("framework").path(
+            "bundles"
+        ).path("representations");
+
+        Response response = target.request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_JSON
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isArray(),
+            j -> j.node("[0]").and(
+                j1 -> j1.node("id").isNumber(),
+                j1 -> j1.node("lastModified").isNumber(),
+                j1 -> j1.node("state").isNumber(),
+                j1 -> j1.node("symbolicName").isString(),
+                j1 -> j1.node("version").isString()
+            )
+        );
+    }
+
+    @Test
+    public void getBundleDTOsXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles").path("representations");
+
+        Response response = target.request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_XML
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//bundles/bundle[2]/id"
+        ).contains("1");
+    }
+
+    @Test
+    public void getBundleDTOs_ListJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        List<BundleDTO> bundleDTOList = response.readEntity(new GenericType<List<BundleDTO>>() {});
+
+        assertThat(
+            bundleDTOList
+        ).isNotEmpty();
+    }
+
+    @Test
+    public void getBundleDTOs_ListJSON_FilterError() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "osgi.wiring.package", "osgi.wiring.package=org.*"
+        ).request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        assertThat(
+            response.getStatus()
+        ).isEqualTo(400);
+    }
+
+    @Test
+    public void getBundleDTOs_ListJSON_Filter_nomatch() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "osgi.wiring.package", "(osgi.wiring.package=org.osgi.service.jaxrs)"
+        ).request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        List<BundleDTO> bundleDTOList = response.readEntity(new GenericType<List<BundleDTO>>() {});
+
+        assertThat(
+            bundleDTOList
+        ).hasSize(0);
+    }
+
+    @Test
+    public void getBundleDTOs_ListJSON_Filter() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "osgi.wiring.package", "(osgi.wiring.package=org.osgi.service.*)"
+        ).request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        List<BundleDTO> bundleDTOList = response.readEntity(new GenericType<List<BundleDTO>>() {});
+
+        assertThat(
+            bundleDTOList
+        ).hasSize(5);
+    }
+
+    @Test
+    public void getBundleDTOs_ListJSON_FilterOr() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "osgi.wiring.package", "(osgi.wiring.package=org.osgi.service.jaxrs.whiteboard)"
+        ).queryParam(
+            "osgi.wiring.package", "(osgi.wiring.package=org.osgi.service.cm)"
+        ).request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        List<BundleDTO> bundleDTOList = response.readEntity(new GenericType<List<BundleDTO>>() {});
+
+        assertThat(
+            bundleDTOList
+        ).hasSize(2);
+    }
+
+    @Test
+    public void getBundleDTOs_ListJSON_FilterAnd() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "osgi.wiring.package", "(osgi.wiring.package=org.osgi.service.*)"
+        ).queryParam(
+            "osgi.identity", "(!(osgi.identity=org.osgi.*))"
+        ).request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        List<BundleDTO> bundleDTOList = response.readEntity(new GenericType<List<BundleDTO>>() {});
+
+        assertThat(
+            bundleDTOList
+        ).hasSize(3);
+    }
+
+    @Test
+    public void getBundleDTOs_ListXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("bundles").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request(
+            APPLICATION_BUNDLES_REPRESENTATIONS_XML_TYPE
+        ).get();
+
+        List<BundleDTO> bundleDTOList = response.readEntity(new GenericType<List<BundleDTO>>() {});
+
+        assertThat(
+            bundleDTOList
+        ).isNotEmpty();
+    }
+
+    @Test
+    public void postBundlesByLocation() throws Exception {
+        try (HttpServer server = new HttpServer(
+                BundlesTest.class.getClassLoader().getResource("minor-change-1.0.1.jar"),
+                "application/zip")) {
+
+            WebTarget target = createDefaultTarget().path(
+                "framework").path("bundles");
+
+            target.register(RestManagementMessageBodyHandler.class);
+
+            BundleDTO bundleDTO = null;
+
+            try {
+                Response response = target.request().post(
+                    Entity.entity(String.format(
+                        "http://localhost:%d/minor-change-1.0.1.jar",
+                        server.getPort()),
+                    MediaType.TEXT_PLAIN));
+
+                assertThat(
+                    response.getStatus()
+                ).isEqualTo(
+                    200
+                );
+
+                bundleDTO = response.readEntity(BundleDTO.class);
+
+                assertThat(
+                    bundleDTO.symbolicName
+                ).isEqualTo(
+                    "minor-and-removed-change"
+                );
+            }
+            finally {
+                if (bundleDTO != null) {
+                    bundleContext.getBundle(bundleDTO.id).uninstall();
+                }
+            }
+        }
+    }
+
+    @Test
+    public void postBundlesByUpload() throws Exception {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundles");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        BundleDTO bundleDTO = null;
+
+        try {
+            Response response = target.request().header(
+                HttpHeaders.CONTENT_LOCATION, "minor-change-1.0.1.jar"
+            ).post(
+                Entity.entity(
+                    BundlesTest.class.getClassLoader().getResourceAsStream("minor-change-1.0.1.jar"),
+                    APPLICATION_VNDOSGIBUNDLE_TYPE)
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleDTO = response.readEntity(BundleDTO.class);
+
+            assertThat(
+                bundleDTO.symbolicName
+            ).isEqualTo(
+                "minor-and-removed-change"
+            );
+        }
+        finally {
+            if (bundleDTO != null) {
+                bundleContext.getBundle(bundleDTO.id).uninstall();
+            }
+        }
+    }
+
+    @Test
+    public void postBundlesByUploadNoContentLocation() throws Exception {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("bundles");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        BundleDTO bundleDTO = null;
+
+        try {
+            Response response = target.request().post(
+                Entity.entity(
+                    BundlesTest.class.getClassLoader().getResourceAsStream("minor-change-1.0.1.jar"),
+                    APPLICATION_VNDOSGIBUNDLE_TYPE)
+            );
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                200
+            );
+
+            bundleDTO = response.readEntity(BundleDTO.class);
+
+            assertThat(
+                bundleDTO.symbolicName
+            ).isEqualTo(
+                "minor-and-removed-change"
+            );
+            BundleAssert.assertThat(
+                bundleContext.getBundle(bundleDTO.id)
+            ).hasLocationThat().contains(
+                "org.apache.aries.jax.rs.whiteboard:"
+            );
+        }
+        finally {
+            if (bundleDTO != null) {
+                bundleContext.getBundle(bundleDTO.id).uninstall();
+            }
+        }
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkStartLevelTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkStartLevelTest.java
new file mode 100644
index 0000000..0ee2a79
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkStartLevelTest.java
@@ -0,0 +1,258 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_FRAMEWORKSTARTLEVEL_XML_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.math.BigDecimal;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.junit.Test;
+import org.osgi.framework.startlevel.dto.FrameworkStartLevelDTO;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class FrameworkStartLevelTest extends TestUtil {
+
+    @Test
+    public void getFrameworkStartLevelJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("startLevel").isNumber().isBetween(new BigDecimal(0), new BigDecimal(Integer.MAX_VALUE)),
+            j -> j.node("initialBundleStartLevel").isNumber().isBetween(new BigDecimal(0), new BigDecimal(Integer.MAX_VALUE))
+        );
+    }
+
+    @Test
+    public void getFrameworkStartLevelJSON_DOT() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel.json");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("startLevel").isNumber().isBetween(new BigDecimal(0), new BigDecimal(Integer.MAX_VALUE)),
+            j -> j.node("initialBundleStartLevel").isNumber().isBetween(new BigDecimal(0), new BigDecimal(Integer.MAX_VALUE))
+        );
+    }
+
+    @Test
+    public void getFrameworkStartLevelJSONExplicit() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel");
+
+        Response response = target.request(
+            APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("startLevel").isNumber().isBetween(new BigDecimal(0), new BigDecimal(Integer.MAX_VALUE)),
+            j -> j.node("initialBundleStartLevel").isNumber().isBetween(new BigDecimal(0), new BigDecimal(Integer.MAX_VALUE))
+        );
+    }
+
+    @Test
+    public void getFrameworkStartLevelXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel");
+
+        Response response = target.request(
+            APPLICATION_FRAMEWORKSTARTLEVEL_XML_TYPE).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid(
+        ).valueByXPath(
+            "//frameworkStartLevel/startLevel"
+        ).asInt(
+        ).isBetween(
+            0, Integer.MAX_VALUE
+        );
+    }
+
+    @Test
+    public void getFrameworkStartLevelXML_DOT() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel.xml");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid(
+        ).valueByXPath(
+            "//frameworkStartLevel/startLevel"
+        ).asInt(
+        ).isBetween(
+            0, Integer.MAX_VALUE
+        );
+    }
+
+    @Test
+    public void getFrameworkStartLevelDTO() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request().get();
+
+        FrameworkStartLevelDTO frameworkStartLevelDTO =
+            response.readEntity(FrameworkStartLevelDTO.class);
+
+        assertThat(
+            frameworkStartLevelDTO.startLevel
+        ).isGreaterThan(
+            0
+        );
+        assertThat(
+            frameworkStartLevelDTO.initialBundleStartLevel
+        ).isGreaterThan(
+            0
+        );
+    }
+
+    @Test
+    public void putFrameworkStartLevelBadValues() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        FrameworkStartLevelDTO frameworkStartLevelDTO =
+            new FrameworkStartLevelDTO();
+
+        Response response = target.request().put(
+            Entity.entity(
+                frameworkStartLevelDTO, APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE));
+
+        assertThat(
+            response.getStatus()
+        ).isEqualTo(
+            400
+        );
+    }
+
+    @Test
+    public void putFrameworkStartLevel() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        FrameworkStartLevelDTO frameworkStartLevelDTO =
+            new FrameworkStartLevelDTO();
+
+        frameworkStartLevelDTO.startLevel = 2;
+        frameworkStartLevelDTO.initialBundleStartLevel = 1;
+
+        try {
+            Response response = target.request().put(
+                Entity.entity(
+                    frameworkStartLevelDTO, APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE));
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                204
+            );
+        }
+        finally {
+            frameworkStartLevelDTO.startLevel = 1;
+
+            Response response = target.request().put(
+                Entity.entity(
+                    frameworkStartLevelDTO, APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE));
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                204
+            );
+        }
+    }
+
+    @Test
+    public void putFrameworkStartLevel_2() {
+        WebTarget target = createDefaultTarget().path(
+            "framework").path("startlevel");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        FrameworkStartLevelDTO frameworkStartLevelDTO =
+            new FrameworkStartLevelDTO();
+
+        frameworkStartLevelDTO.startLevel = 1;
+        frameworkStartLevelDTO.initialBundleStartLevel = 2;
+
+        try {
+            Response response = target.request().put(
+                Entity.entity(
+                    frameworkStartLevelDTO, APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE));
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                204
+            );
+        }
+        finally {
+            frameworkStartLevelDTO.initialBundleStartLevel = 1;
+
+            Response response = target.request().put(
+                Entity.entity(
+                    frameworkStartLevelDTO, APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE));
+
+            assertThat(
+                response.getStatus()
+            ).isEqualTo(
+                204
+            );
+        }
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkStateTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkStateTest.java
new file mode 100644
index 0000000..a06908d
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkStateTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTATE_XML_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.apache.aries.jax.rs.rest.management.model.BundleStateDTO;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class FrameworkStateTest extends TestUtil {
+
+    @Test
+    public void getFrameworkStateJSON() {
+        WebTarget target = createDefaultTarget().path("framework").path("state");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("state").isNumber().isEqualByComparingTo("32"),
+            j -> j.node("options").isNumber().isEqualByComparingTo("2")
+        );
+    }
+
+    @Test
+    public void getFrameworkStateJSON_DOT() {
+        WebTarget target = createDefaultTarget().path("framework").path("state.json");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("state").isNumber().isEqualByComparingTo("32"),
+            j -> j.node("options").isNumber().isEqualByComparingTo("2")
+        );
+    }
+
+    @Test
+    public void getFrameworkStateXML() {
+        WebTarget target = createDefaultTarget().path("framework").path("state");
+
+        Response response = target.request(
+            APPLICATION_BUNDLESTATE_XML_TYPE).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//bundleState/state"
+        ).contains("32");
+   }
+
+    @Test
+    public void getFrameworkStateXML_DOT() {
+        WebTarget target = createDefaultTarget().path("framework").path("state.xml");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//bundleState/state"
+        ).contains("32");
+   }
+
+    @Test
+    public void getFrameworkStateDTO() {
+        WebTarget target = createDefaultTarget().path("framework").path("state");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request().get();
+
+        BundleStateDTO bundleStateDTO = response.readEntity(BundleStateDTO.class);
+
+        assertThat(
+            bundleStateDTO.state
+        ).isEqualTo(
+            Bundle.ACTIVE
+        );
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkTest.java
new file mode 100644
index 0000000..0a17541
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/FrameworkTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.osgi.framework.dto.FrameworkDTO;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class FrameworkTest extends TestUtil {
+
+    @Test
+    public void getFrameworkJSON() {
+        WebTarget target = createDefaultTarget().path("framework");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("bundles").isArray(),
+            j -> j.node("bundles[0]").and(
+                j1 -> j1.isObject(),
+                j1 -> j1.node("id").isNumber(),
+                j1 -> j1.node("lastModified").isNumber(),
+                j1 -> j1.node("state").isNumber().isEqualByComparingTo("32"),
+                j1 -> j1.node("symbolicName").isString(),
+                j1 -> j1.node("version").isString()
+            )
+        );
+    }
+
+    @Test
+    public void getFramework_DOT_JSON() {
+        WebTarget target = createDefaultTarget().path("framework.json");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("bundles").isArray(),
+            j -> j.node("bundles[0]").and(
+                j1 -> j1.isObject(),
+                j1 -> j1.node("id").isNumber(),
+                j1 -> j1.node("lastModified").isNumber(),
+                j1 -> j1.node("state").isNumber().isEqualByComparingTo("32"),
+                j1 -> j1.node("symbolicName").isString(),
+                j1 -> j1.node("version").isString()
+            )
+        );
+    }
+
+    @Ignore("This is non-standard anyway, cleanup later")
+    @Test
+    public void getFrameworkXML() {
+        WebTarget target = createDefaultTarget().path("framework");
+
+        Response response = target.request(APPLICATION_XML).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//framework/bundles[1]"
+        ).contains("0");
+   }
+
+    @Ignore("This is non-standard anyway, cleanup later")
+    @Test
+    public void getFrameworkXML_DOT_XML() {
+        WebTarget target = createDefaultTarget().path("framework.xml");
+
+        Response response = target.request().get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//framework/bundles[1]"
+        ).contains("0");
+   }
+
+    @Test
+    public void getFrameworkDTOJSON() {
+        WebTarget target = createDefaultTarget().path("framework");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request().get();
+
+        FrameworkDTO frameworkDTO = response.readEntity(FrameworkDTO.class);
+
+        assertTrue(frameworkDTO.toString(), frameworkDTO.bundles.size() > 0);
+    }
+
+    @Ignore("This is non-standard anyway, cleanup later")
+    @Test
+    public void getFrameworkDTOXML() {
+        WebTarget target = createDefaultTarget().path("framework");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request(APPLICATION_XML).get();
+
+        FrameworkDTO frameworkDTO = response.readEntity(FrameworkDTO.class);
+
+        assertTrue(frameworkDTO.toString(), frameworkDTO.bundles.size() > 0);
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/RestClientTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/RestClientTest.java
new file mode 100644
index 0000000..1da050d
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/RestClientTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.Collection;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.framework.startlevel.dto.FrameworkStartLevelDTO;
+import org.osgi.service.rest.client.RestClient;
+import org.osgi.service.rest.client.RestClientFactory;
+import org.osgi.test.common.annotation.InjectBundleContext;
+import org.osgi.test.common.annotation.InjectService;
+import org.osgi.test.junit4.context.BundleContextRule;
+
+public class RestClientTest extends TestUtil {
+
+    @Rule
+    public BundleContextRule bundleContextRule = new BundleContextRule();
+
+    @InjectBundleContext
+    BundleContext bundleContext;
+
+    @InjectService
+    public RestClientFactory restClientFactory;
+
+    @Test
+    public void getFrameworkStartLevel() throws Exception {
+        RestClient restClient = restClientFactory.createRestClient(
+            runtimeURI());
+
+        FrameworkStartLevelDTO frameworkStartLevel =
+            restClient.getFrameworkStartLevel();
+
+        assertThat(
+            frameworkStartLevel.startLevel
+        ).isGreaterThan(0);
+        assertThat(
+            frameworkStartLevel.initialBundleStartLevel
+        ).isGreaterThan(0);
+    }
+
+    @Test
+    public void setFrameworkStartLevel_UnsetValues() throws Exception {
+        RestClient restClient = restClientFactory.createRestClient(
+            runtimeURI());
+
+        FrameworkStartLevelDTO frameworkStartLevel = new FrameworkStartLevelDTO();
+
+        assertThatThrownBy(
+            () -> restClient.setFrameworkStartLevel(frameworkStartLevel)
+        );
+    }
+
+    @Test
+    public void setFrameworkStartLevel_CorrectValues() throws Exception {
+        RestClient restClient = restClientFactory.createRestClient(
+            runtimeURI());
+
+        FrameworkStartLevelDTO frameworkStartLevel = new FrameworkStartLevelDTO();
+
+        frameworkStartLevel.initialBundleStartLevel = 2;
+        frameworkStartLevel.startLevel = 1;
+
+        restClient.setFrameworkStartLevel(frameworkStartLevel);
+    }
+
+    @Test
+    public void getBundlePaths() throws Exception {
+        RestClient restClient = restClientFactory.createRestClient(
+            runtimeURI());
+
+        Collection<String> bundlePaths = restClient.getBundlePaths();
+
+        assertThat(
+            bundlePaths.iterator().next()
+        ).contains("framework/bundle/0");
+    }
+
+    @Test
+    public void getBundleById() throws Exception {
+        RestClient restClient = restClientFactory.createRestClient(
+            runtimeURI());
+
+        BundleDTO bundleDTO = restClient.getBundle(1l);
+
+        assertThat(bundleDTO).hasFieldOrPropertyWithValue("id", 1l);
+    }
+
+    @Test
+    public void getBundleByInvalidId() throws Exception {
+        RestClient restClient = restClientFactory.createRestClient(
+            runtimeURI());
+
+        assertThatThrownBy(() ->
+            restClient.getBundle(9999999l)
+        );
+    }
+
+    @Test
+    public void getBundleByPath() throws Exception {
+        RestClient restClient = restClientFactory.createRestClient(
+            runtimeURI());
+
+        BundleDTO bundleDTO = restClient.getBundle("/framework/bundle/0");
+
+        assertThat(
+            bundleDTO
+        ).hasFieldOrPropertyWithValue(
+            "id", 0l
+        ).hasFieldOrProperty(
+            "lastModified"
+        ).hasFieldOrPropertyWithValue(
+            "state", 32
+        ).hasFieldOrPropertyWithValue(
+            "symbolicName", "org.eclipse.osgi"
+        ).hasFieldOrProperty(
+            "version"
+        );
+    }
+
+    @Test
+    public void installBundleByLocation() throws Exception {
+        try (HttpServer server = new HttpServer(
+                BundleTest.class.getClassLoader().getResource("minor-change-1.0.1.jar"),
+                "application/zip")) {
+
+            BundleDTO bundleDTO = null;
+
+            try {
+                RestClient restClient = restClientFactory.createRestClient(
+                    runtimeURI());
+
+                bundleDTO = restClient.installBundle(
+                    String.format(
+                        "http://localhost:%d/minor-change-1.0.1.jar",
+                        server.getPort()
+                    )
+                );
+
+                assertThat(
+                    bundleDTO.symbolicName
+                ).isEqualTo(
+                    "minor-and-removed-change"
+                );
+            }
+            finally {
+                if (bundleDTO != null) {
+                    bundleContext.getBundle(bundleDTO.id).uninstall();
+                }
+            }
+        }
+    }
+
+    @Test
+    public void installBundleByBadLocation() throws Exception {
+        RestClient restClient = restClientFactory.createRestClient(
+            runtimeURI());
+
+        assertThatThrownBy(() -> {
+            restClient.installBundle(
+                "http://localhost:%d/minor-change-1.0.1.jar"
+            );
+        }).isInstanceOf(
+            Exception.class
+        ).hasMessageContaining(
+            "Bad Request"
+        );
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/ServicesTest.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/ServicesTest.java
new file mode 100644
index 0000000..76a1da0
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/ServicesTest.java
@@ -0,0 +1,576 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICES_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICES_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICE_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICE_XML_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.data.MapEntry.entry;
+
+import java.util.Map;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.apache.aries.jax.rs.rest.management.model.ServiceReferenceDTOs;
+import org.apache.aries.jax.rs.rest.management.model.ServicesDTO;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.dto.ServiceReferenceDTO;
+import org.osgi.test.common.annotation.InjectBundleContext;
+import org.osgi.test.junit4.context.BundleContextRule;
+import org.xmlunit.assertj3.XmlAssert;
+
+import net.javacrumbs.jsonunit.assertj.JsonAssertions;
+
+public class ServicesTest extends TestUtil {
+
+    @Rule
+    public BundleContextRule bundleContextRule = new BundleContextRule();
+
+    @InjectBundleContext
+    BundleContext bundleContext;
+
+    @Test
+    public void getServicesJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services");
+
+        Response response = target.request(
+            APPLICATION_SERVICES_JSON_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("services").isArray(),
+            j -> j.node("services[0]").isString().contains("http://", "/rms/framework/service/")
+        );
+    }
+
+    @Test
+    public void getServicesXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services");
+
+        Response response = target.request(
+            APPLICATION_SERVICES_XML_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//services/uri"
+        ).contains("/framework/service/");
+    }
+
+    @Test
+    public void getServicesDTOJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request(
+            APPLICATION_SERVICES_JSON_TYPE
+        ).get();
+
+        ServicesDTO servicesDTO = response.readEntity(ServicesDTO.class);
+
+        assertThat(
+            servicesDTO.services
+        ).hasSizeGreaterThan(60).element(0).asString().contains(
+            "framework/service/"
+        );
+    }
+
+    @Test
+    public void getServicesDTOXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request(
+            APPLICATION_SERVICES_XML_TYPE
+        ).get();
+
+        ServicesDTO servicesDTO = response.readEntity(ServicesDTO.class);
+
+        assertThat(
+            servicesDTO.services
+        ).hasSizeGreaterThan(60).element(0).asString().contains(
+            "framework/service/"
+        );
+    }
+
+    //////////
+
+    @Test
+    public void getServiceRepresentationsJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services").path("representations");
+
+        Response response = target.request(
+            APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("services").isArray(),
+            j -> j.node("services[0]").and(
+                j1 -> j1.isObject(),
+                j1 -> j1.node("id").isNumber(),
+                j1 -> j1.node("properties").and(
+                    j2 -> j2.isObject(),
+                    j2 -> j2.node("objectClass").isArray()
+                ),
+                j1 -> j1.node("bundle").isString().contains("http://", "/rms/framework/bundle/"),
+                j1 -> j1.node("usingBundles").isArray()
+            )
+        );
+    }
+
+    @Test
+    public void getServiceRepresentationsXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services").path("representations");
+
+        Response response = target.request(
+            APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().hasXPath(
+            "//services/service/id"
+        );
+
+        XmlAssert.assertThat(
+            result
+        ).hasXPath(
+            "//services/service/properties"
+        );
+
+        XmlAssert.assertThat(
+            result
+        ).hasXPath(
+            "//services/service/bundle"
+        );
+
+        XmlAssert.assertThat(
+            result
+        ).hasXPath(
+            "//services/service/usingBundles"
+        );
+    }
+
+    @Test
+    public void getServiceRepresentationsDTOJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request(
+            APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        ServiceReferenceDTOs serviceReferenceDTOs = response.readEntity(ServiceReferenceDTOs.class);
+
+        assertThat(
+            serviceReferenceDTOs.services
+        ).isNotEmpty();
+    }
+
+    @Test
+    public void getServiceRepresentationsDTOXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.request(
+            APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE
+        ).get();
+
+        ServiceReferenceDTOs serviceReferenceDTOs = response.readEntity(ServiceReferenceDTOs.class);
+
+        assertThat(
+            serviceReferenceDTOs.services
+        ).isNotEmpty();
+    }
+
+    //////////
+
+    @Test
+    public void getServiceJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("service").path("{serviceid}");
+
+        Response response = target.resolveTemplate(
+            "serviceid", 1l
+        ).request(
+            APPLICATION_SERVICE_JSON_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j1 -> j1.node("id").isNumber().isEqualByComparingTo("1"),
+            j1 -> j1.node("properties").and(
+                j2 -> j2.isObject(),
+                j2 -> j2.node("objectClass").isArray().contains("org.osgi.service.log.LogReaderService", "org.eclipse.equinox.log.ExtendedLogReaderService"),
+                j2 -> j2.node("service\\.scope").isString().contains("bundle"),
+                j2 -> j2.node("service\\.id").isNumber().isEqualByComparingTo("1"),
+                j2 -> j2.node("service\\.bundleid").isNumber().isEqualByComparingTo("0")
+            ),
+            j1 -> j1.node("bundle").isString().contains("http://", "/rms/framework/bundle/"),
+            j1 -> j1.node("usingBundles").isArray()
+        );
+    }
+
+    @Test
+    public void getServiceXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("service").path("{serviceid}");
+
+        Response response = target.resolveTemplate(
+            "serviceid", 1l
+        ).request(
+            APPLICATION_SERVICE_XML_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//service/id"
+        ).isEqualTo("1");
+
+        XmlAssert.assertThat(
+            result
+        ).valueByXPath(
+            "//service/properties/property[3]/@name"
+        ).isEqualTo("service.id");
+
+        XmlAssert.assertThat(
+            result
+        ).valueByXPath(
+            "//service/properties/property[3]/@type"
+        ).isEqualTo("Long");
+
+        XmlAssert.assertThat(
+            result
+        ).valueByXPath(
+            "//service/properties/property[3]/@value"
+        ).isEqualTo("1");
+    }
+
+    @Test
+    public void getServiceDTOJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("service").path("{serviceid}");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.resolveTemplate(
+            "serviceid", 1l
+        ).request(
+            APPLICATION_SERVICE_JSON_TYPE
+        ).get();
+
+        ServiceReferenceDTO serviceReferenceDTO = response.readEntity(ServiceReferenceDTO.class);
+
+        assertThat(
+            serviceReferenceDTO.id
+        ).isEqualTo(
+            1l
+        );
+    }
+
+    @Test
+    public void getServiceDTOXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("service").path("{serviceid}");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.resolveTemplate(
+            "serviceid", 1l
+        ).request(
+            APPLICATION_SERVICE_XML_TYPE
+        ).get();
+
+        ServiceReferenceDTO serviceReferenceDTO = response.readEntity(ServiceReferenceDTO.class);
+
+        assertThat(
+            serviceReferenceDTO.id
+        ).isEqualTo(
+            1l
+        );
+    }
+
+    @Ignore("These are just tests for property coercion")
+    @Test
+    public void getService28XML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("service").path("{serviceid}");
+
+        Response response = target.resolveTemplate(
+            "serviceid", 28l
+        ).request(
+            APPLICATION_SERVICE_XML_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//service/id"
+        ).isEqualTo("28");
+    }
+
+    @Ignore("These are just tests for property coercion")
+    @Test
+    public void getService28DTOXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("service").path("{serviceid}");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.resolveTemplate(
+            "serviceid", 28l
+        ).request(
+            APPLICATION_SERVICE_XML_TYPE
+        ).get();
+
+        ServiceReferenceDTO serviceReferenceDTO = response.readEntity(ServiceReferenceDTO.class);
+
+        assertThat(
+            serviceReferenceDTO.id
+        ).isEqualTo(
+            28l
+        );
+    }
+
+    ////////////
+
+    @Test
+    public void getServicesFilterJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services");
+
+        Response response = target.queryParam(
+            "filter", "(service.id=25)"
+        ).request(
+            APPLICATION_SERVICES_JSON_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        JsonAssertions.assertThatJson(
+            result
+        ).and(
+            j -> j.isObject(),
+            j -> j.node("services").and(
+                j1 -> j1.isArray().hasSize(1).element(0).asString().contains("http://", "/rms/framework/service/25")
+            )
+        );
+    }
+
+    @Test
+    public void getServicesFilterXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services");
+
+        Response response = target.queryParam(
+            "filter", "(service.id=25)"
+        ).request(
+            APPLICATION_SERVICES_XML_TYPE
+        ).get();
+
+        String result = response.readEntity(String.class);
+
+        XmlAssert.assertThat(
+            result
+        ).isInvalid().valueByXPath(
+            "//services/uri"
+        ).contains("framework/service/25");
+    }
+
+    @Test
+    public void getServicesFilterDTOJSON() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "filter", "(service.id=25)"
+        ).request(
+            APPLICATION_SERVICES_JSON_TYPE
+        ).get();
+
+        ServicesDTO servicesDTO = response.readEntity(ServicesDTO.class);
+
+        assertThat(
+            servicesDTO.services.get(0)
+        ).contains(
+            "framework/service/25"
+        );
+    }
+
+    @Test
+    public void getServicesFilterDTOXML() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "filter", "(service.id=25)"
+        ).request(
+            APPLICATION_SERVICES_XML_TYPE
+        ).get();
+
+        ServicesDTO servicesDTO = response.readEntity(ServicesDTO.class);
+
+        assertThat(
+            servicesDTO.services.get(0)
+        ).contains(
+            "framework/service/25"
+        );
+    }
+
+    @Test
+    public void getServicesFilterDTOJSON_2() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "filter", "(objectClass=org.apache.aries.jax.rs.*)"
+        ).request(
+            APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        ServiceReferenceDTOs serviceReferenceDTOs = response.readEntity(ServiceReferenceDTOs.class);
+
+        assertThat(
+            serviceReferenceDTOs.services.get(0)
+        ).hasFieldOrProperty(
+            "bundle"
+        ).hasFieldOrProperty(
+            "id"
+        ).hasFieldOrProperty(
+            "properties"
+        ).hasFieldOrProperty(
+            "usingBundles"
+        );
+
+        Map<String, Object> properties = serviceReferenceDTOs.services.get(0).properties;
+
+        assertThat(properties).contains(
+            entry("objectClass", new String[] {"org.apache.aries.jax.rs.rest.management.internal.FrameworkResource"})
+        ).contains(
+            entry("osgi.jaxrs.application.select", "(osgi.jaxrs.name=RestManagementApplication)")
+        );
+    }
+
+    @Test
+    public void getServicesFilterDTOJSON_caseInsensitiveMatching() {
+        WebTarget target = createDefaultTarget().path(
+            "framework"
+        ).path("services").path("representations");
+
+        target.register(RestManagementMessageBodyHandler.class);
+
+        Response response = target.queryParam(
+            "filter", "(objectclass=org.apache.aries.jax.rs.*)"
+        ).request(
+            APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE
+        ).get();
+
+        ServiceReferenceDTOs serviceReferenceDTOs = response.readEntity(ServiceReferenceDTOs.class);
+
+        assertThat(
+            serviceReferenceDTOs.services.get(0)
+        ).hasFieldOrProperty(
+            "bundle"
+        ).hasFieldOrProperty(
+            "id"
+        ).hasFieldOrProperty(
+            "properties"
+        ).hasFieldOrProperty(
+            "usingBundles"
+        );
+
+        Map<String, Object> properties = serviceReferenceDTOs.services.get(0).properties;
+
+        assertThat(properties).contains(
+            entry("objectClass", new String[] {"org.apache.aries.jax.rs.rest.management.internal.FrameworkResource"})
+        ).contains(
+            entry("osgi.jaxrs.application.select", "(osgi.jaxrs.name=RestManagementApplication)")
+        );
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/TestUtil.java b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/TestUtil.java
new file mode 100644
index 0000000..53df239
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/java/org/apache/aries/jax/rs/rest/management/test/TestUtil.java
@@ -0,0 +1,168 @@
+/*
+ * 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.aries.jax.rs.rest.management.test;
+
+import static org.osgi.service.jaxrs.runtime.JaxrsServiceRuntimeConstants.JAX_RS_SERVICE_ENDPOINT;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Collection;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.junit.Rule;
+import org.osgi.service.jaxrs.runtime.JaxrsServiceRuntime;
+import org.osgi.test.common.annotation.InjectService;
+import org.osgi.test.common.service.ServiceAware;
+import org.osgi.test.junit4.service.ServiceRule;
+
+public class TestUtil {
+    @Rule
+    public ServiceRule serviceRule = new ServiceRule();
+
+    @InjectService
+    public ClientBuilder clientBuilder;
+
+    @InjectService(filter = "(%s=*)", filterArguments = JAX_RS_SERVICE_ENDPOINT)
+    public ServiceAware<JaxrsServiceRuntime> jaxrsServiceRuntimeAware;
+
+    protected WebTarget createDefaultTarget() {
+        clientBuilder.connectTimeout(600000, TimeUnit.SECONDS);
+        clientBuilder.readTimeout(600000, TimeUnit.SECONDS);
+
+        Client client = clientBuilder.build();
+
+        client.register(RestManagementMessageBodyHandler.class);
+
+        return client.target(runtimeURI());
+    }
+
+    protected URI runtimeURI() {
+        return runtimeURI("rms");
+    }
+
+    protected URI runtimeURI(String... parts) {
+        String[] runtimes = canonicalize(
+            jaxrsServiceRuntimeAware.getServiceReference().getProperty(
+                JAX_RS_SERVICE_ENDPOINT));
+
+        if (runtimes.length == 0) {
+            throw new IllegalStateException(
+                "No runtimes could be found on \"osgi.jaxrs.endpoint\" " +
+                    "runtime service property ");
+        }
+
+        String uriString = runtimes[0];
+
+        for (String part : parts) {
+            uriString += part + "/";
+        }
+
+        return URI.create(uriString);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static String[] canonicalize(Object propertyValue) {
+        if (propertyValue == null) {
+            return new String[0];
+        }
+        if (propertyValue instanceof String[]) {
+            return (String[]) propertyValue;
+        }
+        if (propertyValue instanceof Collection) {
+            return ((Collection<String>) propertyValue).toArray(new String[0]);
+        }
+
+        return new String[]{propertyValue.toString()};
+    }
+
+    @SuppressWarnings("restriction")
+    public static class HttpServer implements AutoCloseable {
+
+        private final com.sun.net.httpserver.HttpServer server;
+
+        public HttpServer(URL resource, String contentType) throws IOException {
+            server = com.sun.net.httpserver.HttpServer.create(
+                new InetSocketAddress("localhost", 0), 0);
+
+            server.createContext("/", exchange -> {
+                String requestURI = exchange.getRequestURI().getPath();
+
+                if (!requestURI.equals(resource.getPath())) {
+                    sendError(exchange, 404, "File not found");
+                    return;
+                }
+
+                URLConnection connection = resource.openConnection();
+                exchange.getResponseHeaders().set(
+                    "Content-Type", contentType);
+                exchange.sendResponseHeaders(
+                    200, connection.getContentLength());
+
+                OutputStream out = exchange.getResponseBody();
+                InputStream in = connection.getInputStream();
+                byte[] buf = new byte[4096];
+                int n;
+                while ((n = in.read(buf)) >= 0) {
+                    out.write(buf, 0, n);
+                }
+                out.close();
+                in.close();
+            });
+            server.setExecutor(Executors.newSingleThreadExecutor());
+            server.start();
+        }
+
+        private void sendError(
+                com.sun.net.httpserver.HttpExchange exchange, int code,
+                String description)
+            throws IOException {
+
+            String message = "HTTP error " + code + ": " + description;
+            byte[] messageBytes = message.getBytes("UTF-8");
+
+            exchange.getResponseHeaders().set(
+                "Content-Type", "text/plain; charset=utf-8");
+            exchange.sendResponseHeaders(code, messageBytes.length);
+            OutputStream out = exchange.getResponseBody();
+            out.write(messageBytes);
+            out.close();
+        }
+
+        public int getPort() {
+            return server.getAddress().getPort();
+        }
+
+        @Override
+        public void close() {
+            server.stop(0);
+        }
+
+    }
+
+}
diff --git a/integrations/rest-management/rest-management-itest/src/main/resources/minor-change-1.0.1.jar b/integrations/rest-management/rest-management-itest/src/main/resources/minor-change-1.0.1.jar
new file mode 100644
index 0000000..e07e986
--- /dev/null
+++ b/integrations/rest-management/rest-management-itest/src/main/resources/minor-change-1.0.1.jar
Binary files differ
diff --git a/integrations/rest-management/rest-management/LICENSE b/integrations/rest-management/rest-management/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/integrations/rest-management/rest-management/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.
diff --git a/integrations/rest-management/rest-management/bnd.bnd b/integrations/rest-management/rest-management/bnd.bnd
new file mode 100644
index 0000000..91bbb64
--- /dev/null
+++ b/integrations/rest-management/rest-management/bnd.bnd
@@ -0,0 +1,24 @@
+#    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.
+
+Import-Package: \
+	javax.xml.bind;version="[2.3.0,3)",\
+	javax.xml.bind.annotation;version="[2.3.0,3)",\
+	javax.xml.bind.annotation.adapters;version="[2.3.0,3)",\
+	*
+
+-conditionalpackage: org.apache.aries.component.dsl.*
diff --git a/integrations/rest-management/rest-management/pom.xml b/integrations/rest-management/rest-management/pom.xml
new file mode 100644
index 0000000..34c0cdb
--- /dev/null
+++ b/integrations/rest-management/rest-management/pom.xml
@@ -0,0 +1,62 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.aries.jax.rs</groupId>
+        <artifactId>org.apache.aries.jax.rs</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../../..</relativePath>
+    </parent>
+
+    <artifactId>org.apache.aries.jax.rs.rest.management</artifactId>
+    <name>Apache Aries JAX-RS OSGi REST Management</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.aries.component-dsl</groupId>
+            <artifactId>org.apache.aries.component-dsl.component-dsl</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.jax.rs</groupId>
+            <artifactId>org.apache.aries.jax.rs.openapi.resource</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.rest</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.namespace.implementation</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/RestManagementConstants.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/RestManagementConstants.java
new file mode 100644
index 0000000..ac7003a
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/RestManagementConstants.java
@@ -0,0 +1,162 @@
+/*
+ * 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.aries.jax.rs.rest.management;
+
+import javax.ws.rs.core.MediaType;
+
+public class RestManagementConstants {
+
+    public static final String APPLICATION_BUNDLE_JSON =
+        "application/org.osgi.bundle+json";
+
+    public static final MediaType APPLICATION_BUNDLE_JSON_TYPE =
+        new MediaType("application", "org.osgi.bundle+json");
+
+    public static final String APPLICATION_BUNDLE_XML =
+        "application/org.osgi.bundle+xml";
+
+    public static final MediaType APPLICATION_BUNDLE_XML_TYPE =
+        new MediaType("application", "org.osgi.bundle+xml");
+
+    public static final String APPLICATION_BUNDLEEXCEPTION_JSON =
+        "application/org.osgi.bundleexception+json";
+
+    public static final MediaType APPLICATION_BUNDLEEXCEPTION_JSON_TYPE =
+        new MediaType("application", "org.osgi.bundleexception+json");
+
+    public static final String APPLICATION_BUNDLEEXCEPTION_XML =
+        "application/org.osgi.bundleexception+xml";
+
+    public static final MediaType APPLICATION_BUNDLEEXCEPTION_XML_TYPE =
+        new MediaType("application", "org.osgi.bundleexception+xml");
+
+    public static final String APPLICATION_BUNDLEHEADER_JSON =
+        "application/org.osgi.bundleheader+json";
+
+    public static final MediaType APPLICATION_BUNDLEHEADER_JSON_TYPE =
+        new MediaType("application", "org.osgi.bundleheader+json");
+
+    public static final String APPLICATION_BUNDLEHEADER_XML =
+        "application/org.osgi.bundleheader+xml";
+
+    public static final MediaType APPLICATION_BUNDLEHEADER_XML_TYPE =
+        new MediaType("application", "org.osgi.bundleheader+xml");
+
+    public static final String APPLICATION_BUNDLES_JSON =
+        "application/org.osgi.bundles+json";
+
+    public static final MediaType APPLICATION_BUNDLES_JSON_TYPE =
+        new MediaType("application", "org.osgi.bundles+json");
+
+    public static final String APPLICATION_BUNDLES_REPRESENTATIONS_JSON =
+        "application/org.osgi.bundles.representations+json";
+
+    public static final MediaType APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE =
+        new MediaType("application", "org.osgi.bundles.representations+json");
+
+    public static final String APPLICATION_BUNDLES_REPRESENTATIONS_XML =
+        "application/org.osgi.bundles.representations+xml";
+
+    public static final MediaType APPLICATION_BUNDLES_REPRESENTATIONS_XML_TYPE =
+        new MediaType("application", "org.osgi.bundles.representations+xml");
+
+    public static final String APPLICATION_BUNDLES_XML =
+        "application/org.osgi.bundles+xml";
+
+    public static final MediaType APPLICATION_BUNDLES_XML_TYPE =
+        new MediaType("application", "org.osgi.bundles+xml");
+
+    public static final String APPLICATION_BUNDLESTARTLEVEL_JSON =
+        "application/org.osgi.bundlestartlevel+json";
+
+    public static final MediaType APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE =
+        new MediaType("application", "org.osgi.bundlestartlevel+json");
+
+    public static final String APPLICATION_BUNDLESTARTLEVEL_XML =
+        "application/org.osgi.bundlestartlevel+xml";
+
+    public static final MediaType APPLICATION_BUNDLESTARTLEVEL_XML_TYPE =
+        new MediaType("application", "org.osgi.bundlestartlevel+xml");
+
+    public static final String APPLICATION_BUNDLESTATE_JSON =
+        "application/org.osgi.bundlestate+json";
+
+    public static final MediaType APPLICATION_BUNDLESTATE_JSON_TYPE =
+        new MediaType("application", "org.osgi.bundlestate+json");
+
+    public static final String APPLICATION_BUNDLESTATE_XML =
+        "application/org.osgi.bundlestate+xml";
+
+    public static final MediaType APPLICATION_BUNDLESTATE_XML_TYPE =
+        new MediaType("application", "org.osgi.bundlestate+xml");
+
+    public static final String APPLICATION_FRAMEWORKSTARTLEVEL_JSON =
+        "application/org.osgi.frameworkstartlevel+json";
+
+    public static final MediaType APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE =
+        new MediaType("application", "org.osgi.frameworkstartlevel+json");
+
+    public static final String APPLICATION_FRAMEWORKSTARTLEVEL_XML =
+        "application/org.osgi.frameworkstartlevel+xml";
+
+    public static final MediaType APPLICATION_FRAMEWORKSTARTLEVEL_XML_TYPE =
+        new MediaType("application", "org.osgi.frameworkstartlevel+xml");
+
+    public static final String APPLICATION_SERVICE_JSON =
+        "application/org.osgi.service+json";
+
+    public static final MediaType APPLICATION_SERVICE_JSON_TYPE =
+        new MediaType("application", "org.osgi.service+json");
+
+    public static final String APPLICATION_SERVICE_XML =
+        "application/org.osgi.service+xml";
+
+    public static final MediaType APPLICATION_SERVICE_XML_TYPE =
+        new MediaType("application", "org.osgi.service+xml");
+
+    public static final String APPLICATION_SERVICES_JSON =
+        "application/org.osgi.services+json";
+
+    public static final MediaType APPLICATION_SERVICES_JSON_TYPE =
+        new MediaType("application", "org.osgi.services+json");
+
+    public static final String APPLICATION_SERVICES_REPRESENTATIONS_JSON =
+        "application/org.osgi.services.representations+json";
+
+    public static final MediaType APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE =
+        new MediaType("application", "org.osgi.services.representations+json");
+
+    public static final String APPLICATION_SERVICES_REPRESENTATIONS_XML =
+        "application/org.osgi.services.representations+xml";
+
+    public static final MediaType APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE =
+        new MediaType("application", "org.osgi.services.representations+xml");
+
+    public static final String APPLICATION_SERVICES_XML =
+        "application/org.osgi.services+xml";
+
+    public static final MediaType APPLICATION_SERVICES_XML_TYPE =
+        new MediaType("application", "org.osgi.services+xml");
+
+    public static final String APPLICATION_VNDOSGIBUNDLE =
+        "application/vnd.osgi.bundle";
+
+    public static final MediaType APPLICATION_VNDOSGIBUNDLE_TYPE =
+        new MediaType("application", "vnd.osgi.bundle");
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/Coerce.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/Coerce.java
new file mode 100644
index 0000000..249a698
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/Coerce.java
@@ -0,0 +1,229 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.xml.stream.events.Attribute;
+
+import org.osgi.framework.Constants;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+public class Coerce {
+
+    private Coerce() {
+    }
+
+    public static List<String> elements(JsonNode node) {
+        List<String> list = new ArrayList<>();
+        if (node.isArray()) {
+            for (Iterator<JsonNode> itr = node.elements(); itr.hasNext();) {
+                list.add(itr.next().asText());
+            }
+        }
+        else if (node.isValueNode()) {
+            list.add(node.asText());
+        }
+        return list;
+    }
+
+    public static String type(Attribute typeAT, String name) {
+        if (Constants.SERVICE_ID.equals(name) ||
+            Constants.SERVICE_BUNDLEID.equals(name)) {
+            return "Long";
+        }
+        else if (Constants.SERVICE_RANKING.equals(name)) {
+            return "Integer";
+        }
+        else if (Constants.OBJECTCLASS.equals(name) ||
+                 Constants.SERVICE_PID.equals(name) ||
+                 Constants.SERVICE_SCOPE.equals(name)) {
+            return "String";
+        }
+
+        if (typeAT == null) {
+            return "String";
+        }
+        return typeAT.getValue();
+    }
+
+    public static String type(JsonNode node, String name) {
+        if (Constants.SERVICE_ID.equals(name) ||
+            Constants.SERVICE_BUNDLEID.equals(name)) {
+            return "Long";
+        }
+        else if (Constants.SERVICE_RANKING.equals(name)) {
+            return "Integer";
+        }
+        else if (Constants.OBJECTCLASS.equals(name) ||
+                 Constants.SERVICE_PID.equals(name) ||
+                 Constants.SERVICE_SCOPE.equals(name)) {
+            return "String";
+        }
+
+        if (node.isArray()) {
+            if (node.isEmpty()) {
+                return "String";
+            }
+
+            node = node.get(0);
+        }
+
+        if (node.isBoolean()) {
+            return "Boolean";
+        }
+        else if (node.isLong()) {
+            return "Long";
+        }
+        else if (node.isInt()) {
+            return "Integer";
+        }
+        else if (node.isNumber()) {
+            String nodeValue = node.asText();
+            try {
+                Long.parseLong(nodeValue);
+                return "Long";
+            }
+            catch (NumberFormatException nfe1) {
+                try {
+                    Double.parseDouble(nodeValue);
+                    return "Double";
+                }
+                catch (NumberFormatException nfe2) {
+                    throw new IllegalArgumentException(nfe2);
+                }
+            }
+        }
+
+        return "String";
+    }
+
+    public static Object from(Entry<String, JsonNode> entry) {
+        JsonNode valueNode = entry.getValue();
+        return from(
+            Coerce.type(valueNode, entry.getKey()),
+            valueNode.isArray(),
+            Coerce.elements(valueNode)
+        );
+    }
+
+    public static Object from(Attribute keyAT, Attribute typeAT, String txt, boolean array) {
+        List<String> valueParts = Optional.ofNullable(txt).map(
+            s -> s.trim().split("\\s*\\n\\s*")
+        ).map(Arrays::asList).orElseGet(ArrayList::new);
+
+        return from(type(typeAT, keyAT.getValue()), array, valueParts);
+    }
+
+    public static Object from(
+        String type, boolean array, List<String> valueParts) {
+
+        Objects.requireNonNull(type);
+
+        if (array) {
+            if (type.equals("Long")) {
+                return valueParts.stream().map(Long::parseLong).mapToLong(
+                    Long::longValue).toArray();
+            }
+            else if (type.equals("Double")) {
+                return valueParts.stream().map(Double::parseDouble).mapToDouble(
+                    Double::doubleValue).toArray();
+            }
+            else if (type.equals("Float")) {
+                float[] floats = new float[valueParts.size()];
+                for (int i = 0; i < valueParts.size(); i++) {
+                    floats[i] = Float.parseFloat(valueParts.get(i));
+                }
+                return floats;
+            }
+            else if (type.equals("Integer")) {
+                return valueParts.stream().map(Integer::parseInt).mapToInt(
+                    Integer::intValue).toArray();
+            }
+            else if (type.equals("Byte")) {
+                byte[] bytes = new byte[valueParts.size()];
+                for (int i = 0; i < valueParts.size(); i++) {
+                    bytes[i] = Byte.parseByte(valueParts.get(i));
+                }
+                return bytes;
+            }
+            else if (type.equals("Character")) {
+                return valueParts.stream().map(Integer::parseInt).mapToInt(
+                    Integer::intValue).toArray();
+            }
+            else if (type.equals("Boolean")) {
+                boolean[] booleans = new boolean[valueParts.size()];
+                for (int i = 0; i < valueParts.size(); i++) {
+                    booleans[i] = Boolean.parseBoolean(valueParts.get(i));
+                }
+                return booleans;
+            }
+            else if (type.equals("Short")) {
+                short[] shorts = new short[valueParts.size()];
+                for (int i = 0; i < valueParts.size(); i++) {
+                    shorts[i] = Short.parseShort(valueParts.get(i));
+                }
+                return shorts;
+            }
+            else {
+                return valueParts.stream().toArray(String[]::new);
+            }
+        }
+        else {
+            if (valueParts.isEmpty()) {
+                return null;
+            }
+
+            if (type.equals("Long")) {
+                return Long.parseLong(valueParts.get(0));
+            }
+            else if (type.equals("Double")) {
+                return Double.parseDouble(valueParts.get(0));
+            }
+            else if (type.equals("Float")) {
+                return Float.parseFloat(valueParts.get(0));
+            }
+            else if (type.equals("Integer")) {
+                return Integer.parseInt(valueParts.get(0));
+            }
+            else if (type.equals("Byte")) {
+                return Byte.parseByte(valueParts.get(0));
+            }
+            else if (type.equals("Character")) {
+                return Integer.parseInt(valueParts.get(0));
+            }
+            else if (type.equals("Boolean")) {
+                return Boolean.parseBoolean(valueParts.get(0));
+            }
+            else if (type.equals("Short")) {
+                return Short.parseShort(valueParts.get(0));
+            }
+            else {
+                return valueParts.get(0);
+            }
+        }
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/DictionaryJsonDeserializer.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/DictionaryJsonDeserializer.java
new file mode 100644
index 0000000..8fc9f2d
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/DictionaryJsonDeserializer.java
@@ -0,0 +1,50 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+@SuppressWarnings("serial")
+class DictionaryJsonDeserializer extends StdDeserializer<Dictionary<String, String>> {
+
+    protected DictionaryJsonDeserializer() {
+        this(null);
+    }
+
+    protected DictionaryJsonDeserializer(Class<Dictionary<String, String>> t) {
+        super(t);
+    }
+
+    @Override
+    public Dictionary<String, String> deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException {
+
+        Dictionary<String, String> dictionary = new Hashtable<>();
+        JsonNode node = jp.getCodec().readTree(jp);
+        node.fields().forEachRemaining(entry -> dictionary.put(entry.getKey(), entry.getValue().asText()));
+        return dictionary;
+    }
+
+}
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/DictionaryJsonSerializer.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/DictionaryJsonSerializer.java
new file mode 100644
index 0000000..f49b8c2
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/DictionaryJsonSerializer.java
@@ -0,0 +1,64 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Dictionary;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+@SuppressWarnings({ "serial", "rawtypes" })
+class DictionaryJsonSerializer extends StdSerializer<Dictionary> {
+
+    protected DictionaryJsonSerializer() {
+        this(null);
+    }
+
+    protected DictionaryJsonSerializer(Class<Dictionary> t) {
+        super(t);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void serialize(
+            Dictionary value, JsonGenerator gen, SerializerProvider provider)
+        throws IOException {
+
+        gen.writeStartObject();
+
+        Collections.<String>list(value.keys()).stream().sorted().forEach(
+            key -> {
+                try {
+                    gen.writeStringField(
+                        String.valueOf(key),
+                        String.valueOf(value.get(key))
+                    );
+                }
+                catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        );
+
+        gen.writeEndObject();
+    }
+
+}
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/RestManagementMessageBodyHandler.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/RestManagementMessageBodyHandler.java
new file mode 100644
index 0000000..fa1cca5
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/RestManagementMessageBodyHandler.java
@@ -0,0 +1,992 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML_TYPE;
+import static javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLEHEADER_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLEHEADER_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTARTLEVEL_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTATE_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLESTATE_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_REPRESENTATIONS_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLES_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLE_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_BUNDLE_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_FRAMEWORKSTARTLEVEL_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICES_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICES_XML_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICE_JSON_TYPE;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.APPLICATION_SERVICE_XML_TYPE;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.namespace.QName;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.XMLEvent;
+
+import org.apache.aries.jax.rs.rest.management.model.BundleStateDTO;
+import org.apache.aries.jax.rs.rest.management.model.BundlesDTO;
+import org.apache.aries.jax.rs.rest.management.model.ServiceReferenceDTOs;
+import org.apache.aries.jax.rs.rest.management.model.ServicesDTO;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.framework.dto.FrameworkDTO;
+import org.osgi.framework.dto.ServiceReferenceDTO;
+import org.osgi.framework.startlevel.dto.BundleStartLevelDTO;
+import org.osgi.framework.startlevel.dto.FrameworkStartLevelDTO;
+import org.w3c.dom.Element;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+public class RestManagementMessageBodyHandler
+    implements MessageBodyReader<Object>,
+        MessageBodyWriter<Object> {
+
+    private static final TypeReference<Collection<BundleDTO>> BUNDLEDTO_COLLECTION =
+        new TypeReference<Collection<BundleDTO>>() {};
+    private static final TypeReference<Collection<ServiceReferenceDTO>> SERVICEDTO_COLLECTION =
+        new TypeReference<Collection<ServiceReferenceDTO>>() {};
+    private static final TypeReference<Dictionary<String, String>> BUNDLE_HEADER =
+        new TypeReference<Dictionary<String, String>>() {};
+    private final JAXBContext context;
+
+    private final XMLSerializer serializer;
+    private final XMLDeserializer deserializer;
+    private final ServiceReferenceDTOXMLDeserializer serviceReferenceDTO_XMLDeserializer;
+    private final ServiceReferenceDTOXMLSerializer serviceReferenceDTO_XMLSerializer;
+
+    private final Function<UriInfo, ObjectMapper> jsonMapperFunction = uriInfo -> {
+        ObjectMapper jsonMapper = new ObjectMapper().configure(
+            INDENT_OUTPUT, true
+        );
+
+        SimpleModule module = new SimpleModule();
+        module.addSerializer(Dictionary.class, new DictionaryJsonSerializer());
+        module.addDeserializer(Dictionary.class, new DictionaryJsonDeserializer());
+        module.addSerializer(ServiceReferenceDTO.class, new ServiceReferenceDTOJsonSerializer(uriInfo));
+        module.addDeserializer(ServiceReferenceDTO.class, new ServiceReferenceDTOJsonDeserializer());
+
+        return jsonMapper.registerModule(module);
+    };
+
+    @Context
+    volatile UriInfo uriInfo;
+
+    public RestManagementMessageBodyHandler() {
+
+        serializer = new XMLSerializer();
+        deserializer = new XMLDeserializer();
+        serviceReferenceDTO_XMLDeserializer = new ServiceReferenceDTOXMLDeserializer(deserializer);
+        serviceReferenceDTO_XMLSerializer = new ServiceReferenceDTOXMLSerializer(serializer);
+
+        try {
+            context = JAXBContext.newInstance(
+                BundleDTO.class,
+                BundleDTO[].class,
+                BundlesDTO.class,
+                BundleStartLevelDTO.class,
+                BundleStateDTO.class,
+                Dictionary.class,
+                FrameworkStartLevelDTO.class,
+                FrameworkDTO.class,
+                ServicesDTO.class,
+                ServiceReferenceDTO.class,
+                ServiceReferenceDTOs.class
+            );
+        }
+        catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isReadable(
+        Class<?> type, Type genericType, Annotation[] annotations,
+        MediaType mediaType) {
+
+        return
+            (Collection.class.isAssignableFrom(type) &&
+                nthTypeArgumentIs(0, genericType, BundleDTO.class) && (
+                APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_BUNDLES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (Collection.class.isAssignableFrom(type) &&
+                nthTypeArgumentIs(0, genericType, ServiceReferenceDTO.class) && (
+                APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (Dictionary.class.isAssignableFrom(type) &&
+                nthTypeArgumentIs(0, genericType, String.class) &&
+                nthTypeArgumentIs(1, genericType, String.class) && (
+                APPLICATION_BUNDLEHEADER_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_BUNDLEHEADER_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == BundleDTO.class && (
+                APPLICATION_BUNDLE_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_BUNDLE_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == BundlesDTO.class && (
+                APPLICATION_BUNDLES_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_BUNDLES_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == BundleStateDTO.class && (
+                APPLICATION_BUNDLESTATE_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_BUNDLESTATE_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == BundleStartLevelDTO.class && (
+                APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_BUNDLESTARTLEVEL_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == FrameworkStartLevelDTO.class && (
+                APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_FRAMEWORKSTARTLEVEL_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == FrameworkDTO.class && (
+                APPLICATION_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == ServicesDTO.class && (
+                APPLICATION_SERVICES_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_SERVICES_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == ServiceReferenceDTO.class && (
+                APPLICATION_SERVICE_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_SERVICE_XML_TYPE.isCompatible(mediaType)))
+                ||
+            (type == ServiceReferenceDTOs.class && (
+                APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType) ||
+                APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)))
+            ;
+    }
+
+    @Override
+    public boolean isWriteable(
+        Class<?> type, Type genericType, Annotation[] annotations,
+        MediaType mediaType) {
+
+        return isReadable(type, genericType, annotations, mediaType);
+    }
+
+    BundleDTO readBundleDTOFrom(
+            Class<? extends BundleDTO> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders,
+            InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLE_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_BUNDLE_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                return (BundleDTO)context.createUnmarshaller().unmarshal(
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+
+        return null;
+    }
+
+    Collection<BundleDTO> readBundleDTOListFrom(
+            @SuppressWarnings("rawtypes") Class<? extends Collection> type,
+            Type genericType, Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(BUNDLEDTO_COLLECTION).readValue(entityStream);
+        }
+        else if (APPLICATION_BUNDLES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)) {
+            List<BundleDTO> bundles = new ArrayList<>();
+            AtomicReference<BundleDTO> bundle = new AtomicReference<>();
+            deserializer.deserialize((start, next) -> {
+                String el = start.getName().getLocalPart();
+                XMLEvent event = start;
+                if (el.equals("bundle")) {
+                    bundle.set(new BundleDTO());
+                }
+                else if (el.equals("id")) {
+                    try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                    bundle.get().id = Long.parseLong(event.asCharacters().getData());
+                }
+                else if (el.equals("lastModified")) {
+                    try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                    bundle.get().lastModified = Long.parseLong(event.asCharacters().getData());
+                }
+                else if (el.equals("state")) {
+                    try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                    bundle.get().state = Integer.parseInt(event.asCharacters().getData());
+                }
+                else if (el.equals("symbolicName")) {
+                    try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                    bundle.get().symbolicName = event.asCharacters().getData();
+                }
+                else if (el.equals("version")) {
+                    try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                    bundle.get().version = event.asCharacters().getData();
+                }
+                return event;
+            }, end -> {
+                String el = end.getName().getLocalPart();
+                if (el.equals("bundle")) {
+                    bundles.add(bundle.get());
+                    bundle.set(null);
+                }
+            }, entityStream);
+
+            return bundles;
+        }
+
+        return null;
+    }
+
+    Dictionary<String, String> readBundleHeadersFrom(
+            @SuppressWarnings("rawtypes") Class<? extends Dictionary> asSubclass,
+            Type genericType, Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+        throws IOException, WebApplicationException{
+
+        if (APPLICATION_BUNDLEHEADER_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(BUNDLE_HEADER).readValue(entityStream);
+        }
+        else if (APPLICATION_BUNDLEHEADER_XML_TYPE.isCompatible(mediaType)) {
+            Dictionary<String, String> header = new Hashtable<>();
+
+            deserializer.deserialize((start, next) -> {
+                String el = start.getName().getLocalPart();
+                if (el.equals("entry")) {
+                    Attribute keyAT = start.getAttributeByName(new QName("key"));
+                    Attribute valueAT = start.getAttributeByName(new QName("value"));
+                    header.put(keyAT.getValue(), valueAT.getValue());
+                }
+                return start;
+            }, end -> {}, entityStream);
+
+            return header;
+        }
+
+        return null;
+    }
+
+    BundlesDTO readBundlesDTOFrom(
+            Class<? extends BundlesDTO> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders,
+            InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLES_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_BUNDLES_XML_TYPE.isCompatible(mediaType)) {
+            BundlesDTO bundlesDTO = new BundlesDTO();
+            bundlesDTO.bundles = new ArrayList<>();
+
+            deserializer.deserialize((start, next) -> {
+                String el = start.getName().getLocalPart();
+                XMLEvent event = start;
+                if (el.equals("uri")) {
+                    try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                    bundlesDTO.bundles.add(event.asCharacters().getData());
+                }
+                return start;
+            }, end -> {}, entityStream);
+
+            return bundlesDTO;
+        }
+
+        return null;
+    }
+
+    BundleStartLevelDTO readBundleStartLevelDTOFrom(
+            Class<? extends BundleStartLevelDTO> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_BUNDLESTARTLEVEL_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                return (BundleStartLevelDTO)context.createUnmarshaller().unmarshal(
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+
+        return null;
+    }
+
+    BundleStateDTO readBundleStateDTOFrom(
+            Class<? extends BundleStateDTO> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders,
+            InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLESTATE_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_BUNDLESTATE_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                return (BundleStateDTO)context.createUnmarshaller().unmarshal(
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+
+        return null;
+    }
+
+    FrameworkDTO readFrameworkDTOFrom(
+            Class<? extends FrameworkDTO> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders,
+            InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                return (FrameworkDTO)context.createUnmarshaller().unmarshal(
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+
+        return null;
+    }
+
+    FrameworkStartLevelDTO readFrameworkStartLevelDTOFrom(
+            Class<? extends FrameworkStartLevelDTO> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders,
+            InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_FRAMEWORKSTARTLEVEL_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                return (FrameworkStartLevelDTO)context.createUnmarshaller().unmarshal(
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+
+        return null;
+    }
+
+    @Override
+    public Object readFrom(
+            Class<Object> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders,
+            InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (Collection.class.isAssignableFrom(type) &&
+            nthTypeArgumentIs(0, genericType, BundleDTO.class)) {
+
+            return readBundleDTOListFrom(
+                type.asSubclass(Collection.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (Collection.class.isAssignableFrom(type) &&
+            nthTypeArgumentIs(0, genericType, ServiceReferenceDTO.class)) {
+
+            return readServiceReferenceDTOListFrom(
+                type.asSubclass(Collection.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (Dictionary.class.isAssignableFrom(type) &&
+            nthTypeArgumentIs(0, genericType, String.class) &&
+            nthTypeArgumentIs(1, genericType, String.class)) {
+
+            return readBundleHeadersFrom(
+                type.asSubclass(Dictionary.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (BundleDTO.class.equals(type)) {
+            return readBundleDTOFrom(
+                type.asSubclass(BundleDTO.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (BundlesDTO.class.equals(type)) {
+            return readBundlesDTOFrom(
+                type.asSubclass(BundlesDTO.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (BundleStateDTO.class.equals(type)) {
+            return readBundleStateDTOFrom(
+                type.asSubclass(BundleStateDTO.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (BundleStartLevelDTO.class.equals(type)) {
+            return readBundleStartLevelDTOFrom(
+                type.asSubclass(BundleStartLevelDTO.class), genericType,
+                annotations, mediaType, httpHeaders, entityStream);
+        }
+        else if (FrameworkStartLevelDTO.class.equals(type)) {
+            return readFrameworkStartLevelDTOFrom(
+                type.asSubclass(FrameworkStartLevelDTO.class), genericType,
+                annotations, mediaType, httpHeaders, entityStream);
+        }
+        else if (FrameworkDTO.class.equals(type)) {
+            return readFrameworkDTOFrom(
+                type.asSubclass(FrameworkDTO.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (ServicesDTO.class.equals(type)) {
+            return readServicesDTOFrom(
+                type.asSubclass(ServicesDTO.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (ServiceReferenceDTO.class.equals(type)) {
+            return readServiceReferenceDTOFrom(
+                type.asSubclass(ServiceReferenceDTO.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (ServiceReferenceDTOs.class.equals(type)) {
+            return readServiceReferenceDTOsFrom(
+                type.asSubclass(ServiceReferenceDTOs.class), genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+
+        return null;
+    }
+
+    ServiceReferenceDTO readServiceReferenceDTOFrom(
+            Class<? extends ServiceReferenceDTO> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_SERVICE_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_SERVICE_XML_TYPE.isCompatible(mediaType)) {
+            return serviceReferenceDTO_XMLDeserializer.deserializeServiceReferenceDTO(entityStream);
+        }
+
+        return null;
+    }
+
+    Collection<ServiceReferenceDTO> readServiceReferenceDTOListFrom(
+            @SuppressWarnings("rawtypes") Class<? extends Collection> type,
+            Type genericType, Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(SERVICEDTO_COLLECTION).readValue(entityStream);
+        }
+        else if (APPLICATION_BUNDLES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)) {
+            return serviceReferenceDTO_XMLDeserializer.deserializeServiceReferenceDTOList(entityStream);
+        }
+
+        return null;
+    }
+
+    ServiceReferenceDTOs readServiceReferenceDTOsFrom(
+            Class<? extends ServiceReferenceDTOs> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)) {
+            ServiceReferenceDTOs serviceReferenceDTOs = new ServiceReferenceDTOs();
+            serviceReferenceDTOs.services = serviceReferenceDTO_XMLDeserializer.deserializeServiceReferenceDTOList(entityStream);
+            return serviceReferenceDTOs;
+        }
+
+        return null;
+    }
+
+    ServicesDTO readServicesDTOFrom(
+            Class<? extends ServicesDTO> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
+        throws IOException, WebApplicationException{
+
+        if (APPLICATION_SERVICES_JSON_TYPE.isCompatible(mediaType)) {
+            return jsonMapperFunction.apply(uriInfo).readerFor(type).readValue(entityStream);
+        }
+        else if (APPLICATION_SERVICES_XML_TYPE.isCompatible(mediaType)) {
+            ServicesDTO servicesDTO = new ServicesDTO();
+            servicesDTO.services = new ArrayList<>();
+
+            deserializer.deserialize((start, next) -> {
+                String el = start.getName().getLocalPart();
+                XMLEvent event = start;
+                if (el.equals("uri")) {
+                    try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                    servicesDTO.services.add(event.asCharacters().getData());
+                }
+                return start;
+            }, end -> {}, entityStream);
+
+            return servicesDTO;
+        }
+
+        return null;
+    }
+
+    void writeBundleDTOListTo(
+            Collection<BundleDTO> list, Class<?> type, Type genericType,
+            Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, list);
+        }
+        else if (APPLICATION_BUNDLES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                serializer.serialize(
+                    "bundles",
+                    (dom, rootEl) -> list.stream().map(
+                        bundleDTO -> {
+                            Element bundleEl = dom.createElement("bundle");
+
+                            Element idEl = dom.createElement("id");
+                            idEl.setTextContent(String.valueOf(bundleDTO.id));
+                            Element lastModifiedEl = dom.createElement("lastModified");
+                            lastModifiedEl.setTextContent(String.valueOf(bundleDTO.lastModified));
+                            Element stateEl = dom.createElement("state");
+                            stateEl.setTextContent(String.valueOf(bundleDTO.state));
+                            Element symbolicNameEl = dom.createElement("symbolicName");
+                            symbolicNameEl.setTextContent(bundleDTO.symbolicName);
+                            Element versionEl = dom.createElement("version");
+                            versionEl.setTextContent(bundleDTO.version);
+
+                            bundleEl.appendChild(idEl);
+                            bundleEl.appendChild(lastModifiedEl);
+                            bundleEl.appendChild(stateEl);
+                            bundleEl.appendChild(symbolicNameEl);
+                            bundleEl.appendChild(versionEl);
+
+                            return bundleEl;
+                        }
+                    ).forEach(
+                        bundleEL -> rootEl.appendChild(bundleEL)
+                    ),
+                    entityStream
+                );
+            }
+            catch (Exception e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    void writeBundleDTOTo(
+            BundleDTO bundleDTO,
+            Class<?> type, Type genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLE_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, bundleDTO);
+        }
+        else if (APPLICATION_BUNDLE_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                Marshaller marshaller = context.createMarshaller();
+                marshaller.setProperty(JAXB_FORMATTED_OUTPUT, true);
+                marshaller.marshal(
+                    new JAXBElement<>(
+                        new QName("bundle"),
+                        BundleDTO.class,
+                        bundleDTO),
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    void writeBundleHeadersTo(
+            Dictionary<String, String> headers, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException{
+
+        if (APPLICATION_BUNDLEHEADER_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, headers);
+        }
+        else if (APPLICATION_BUNDLEHEADER_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                serializer.serialize(
+                    "bundleHeader",
+                    (dom, rootEl) -> Collections.list(
+                        headers.keys()
+                    ).stream().forEach(
+                        key -> {
+                            Element entryEl = dom.createElement("entry");
+                            entryEl.setAttribute("key", key);
+                            entryEl.setAttribute("value", headers.get(key));
+                            rootEl.appendChild(entryEl);
+                        }
+                    ),
+                    entityStream
+                );
+            }
+            catch (Exception e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    void writeBundlesDTOTo(
+            BundlesDTO bundlesDTO,
+            Class<?> type, Type genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLES_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, bundlesDTO);
+        }
+        else if (APPLICATION_BUNDLES_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                serializer.serialize(
+                    "bundles",
+                    (dom, rootEl) -> bundlesDTO.bundles.stream().forEach(
+                        key -> {
+                            Element entryEl = dom.createElement("uri");
+                            entryEl.setTextContent(key);
+                            rootEl.appendChild(entryEl);
+                        }
+                    ),
+                    entityStream
+                );
+            }
+            catch (Exception e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    void writeBundleStartLevelDTOTo(
+            BundleStartLevelDTO bundleStartLevelDTO, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLESTARTLEVEL_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, bundleStartLevelDTO);
+        }
+        else if (APPLICATION_BUNDLESTARTLEVEL_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                Marshaller marshaller = context.createMarshaller();
+                marshaller.setProperty(JAXB_FORMATTED_OUTPUT, true);
+                marshaller.marshal(
+                    new JAXBElement<>(
+                        new QName("bundleStartLevel"),
+                        BundleStartLevelDTO.class,
+                        bundleStartLevelDTO),
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    void writeBundleStateDTOTo(
+            BundleStateDTO bundleStateDTO,
+            Class<?> type, Type genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_BUNDLESTATE_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, bundleStateDTO);
+        }
+        else if (APPLICATION_BUNDLESTATE_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                Marshaller marshaller = context.createMarshaller();
+                marshaller.setProperty(JAXB_FORMATTED_OUTPUT, true);
+                marshaller.marshal(
+                    new JAXBElement<>(
+                        new QName("bundleState"),
+                        BundleStateDTO.class,
+                        bundleStateDTO),
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    void writeFrameworkDTOTo(
+            FrameworkDTO frameworkDTO,
+            Class<?> type, Type genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, frameworkDTO);
+        }
+        else if (APPLICATION_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                Marshaller marshaller = context.createMarshaller();
+                marshaller.setProperty(JAXB_FORMATTED_OUTPUT, true);
+                marshaller.marshal(
+                    new JAXBElement<>(
+                        new QName("framework"),
+                        FrameworkDTO.class,
+                        frameworkDTO),
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    void writeFrameworkStartLevelDTOTo(
+            FrameworkStartLevelDTO frameworkStartLevelDTO,
+            Class<?> type, Type genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_FRAMEWORKSTARTLEVEL_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, frameworkStartLevelDTO);
+        }
+        else if (APPLICATION_FRAMEWORKSTARTLEVEL_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                Marshaller marshaller = context.createMarshaller();
+                marshaller.setProperty(JAXB_FORMATTED_OUTPUT, true);
+                marshaller.marshal(
+                    new JAXBElement<>(
+                        new QName("frameworkStartLevel"),
+                        FrameworkStartLevelDTO.class,
+                        frameworkStartLevelDTO),
+                    entityStream);
+            } catch (JAXBException e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    void writeServiceReferenceDTOTo(
+            ServiceReferenceDTO serviceReferenceDTO, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_SERVICE_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, serviceReferenceDTO);
+        }
+        else if (APPLICATION_SERVICE_XML_TYPE.isCompatible(mediaType)) {
+            serviceReferenceDTO_XMLSerializer.serialize(serviceReferenceDTO, uriInfo, entityStream);
+        }
+    }
+
+    void writeServiceReferenceDTOListTo(
+            Collection<ServiceReferenceDTO> list, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, list);
+        }
+        else if (APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)) {
+            serviceReferenceDTO_XMLSerializer.serialize(list, uriInfo, entityStream);
+        }
+    }
+
+    void writeServiceReferenceDTOsTo(
+            ServiceReferenceDTOs serviceReferenceDTOs, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_SERVICES_REPRESENTATIONS_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, serviceReferenceDTOs);
+        }
+        else if (APPLICATION_SERVICES_REPRESENTATIONS_XML_TYPE.isCompatible(mediaType)) {
+            serviceReferenceDTO_XMLSerializer.serialize(
+                serviceReferenceDTOs.services, uriInfo, entityStream);
+        }
+    }
+
+    void writeServicesDTOTo(
+            ServicesDTO servicesDTO, Class<?> type, Type genericType,
+            Annotation[] annotations, MediaType mediaType,
+            MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (APPLICATION_SERVICES_JSON_TYPE.isCompatible(mediaType)) {
+            jsonMapperFunction.apply(uriInfo).writeValue(entityStream, servicesDTO);
+        }
+        else if (APPLICATION_SERVICES_XML_TYPE.isCompatible(mediaType)) {
+            try {
+                serializer.serialize(
+                    "services",
+                    (dom, rootEl) -> servicesDTO.services.stream().forEach(
+                        key -> {
+                            Element entryEl = dom.createElement("uri");
+                            entryEl.setTextContent(key);
+                            rootEl.appendChild(entryEl);
+                        }
+                    ),
+                    entityStream
+                );
+            }
+            catch (Exception e) {
+                throw new IOException(e);
+            }
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void writeTo(
+            Object entity,
+            Class<?> type, Type genericType, Annotation[] annotations,
+            MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
+            OutputStream entityStream)
+        throws IOException, WebApplicationException {
+
+        if (Collection.class.isAssignableFrom(type) &&
+            nthTypeArgumentIs(0, genericType, BundleDTO.class)) {
+
+            writeBundleDTOListTo(
+                Collection.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (Collection.class.isAssignableFrom(type) &&
+            nthTypeArgumentIs(0, genericType, ServiceReferenceDTO.class)) {
+
+            writeServiceReferenceDTOListTo(
+                Collection.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (Dictionary.class.isAssignableFrom(type) &&
+            nthTypeArgumentIs(0, genericType, String.class) &&
+            nthTypeArgumentIs(1, genericType, String.class)) {
+
+            writeBundleHeadersTo(
+                Dictionary.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (type == BundleDTO.class) {
+            writeBundleDTOTo(
+                BundleDTO.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (BundlesDTO.class.equals(type)) {
+            writeBundlesDTOTo(
+                BundlesDTO.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (BundleStateDTO.class.equals(type)) {
+            writeBundleStateDTOTo(
+                BundleStateDTO.class.cast(entity), type, genericType,
+                annotations, mediaType, httpHeaders, entityStream);
+        }
+        else if (BundleStartLevelDTO.class.equals(type)) {
+            writeBundleStartLevelDTOTo(
+                BundleStartLevelDTO.class.cast(entity), type, genericType,
+                annotations, mediaType, httpHeaders, entityStream);
+        }
+        else if (FrameworkStartLevelDTO.class.equals(type)) {
+            writeFrameworkStartLevelDTOTo(
+                FrameworkStartLevelDTO.class.cast(entity), type, genericType,
+                annotations, mediaType, httpHeaders, entityStream);
+        }
+        else if (FrameworkDTO.class.equals(type)) {
+            writeFrameworkDTOTo(
+                FrameworkDTO.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (ServiceReferenceDTO.class.equals(type)) {
+            writeServiceReferenceDTOTo(
+                ServiceReferenceDTO.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (ServicesDTO.class.equals(type)) {
+            writeServicesDTOTo(
+                ServicesDTO.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+        else if (ServiceReferenceDTOs.class.equals(type)) {
+            writeServiceReferenceDTOsTo(
+                ServiceReferenceDTOs.class.cast(entity), type, genericType, annotations,
+                mediaType, httpHeaders, entityStream);
+        }
+    }
+
+    private boolean nthTypeArgumentIs(int n, Type type, Class<?> clazz) {
+        return ((ParameterizedType)type).getActualTypeArguments()[n].equals(clazz);
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOJsonDeserializer.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOJsonDeserializer.java
new file mode 100644
index 0000000..f06b666
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOJsonDeserializer.java
@@ -0,0 +1,71 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.osgi.framework.dto.ServiceReferenceDTO;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+@SuppressWarnings("serial")
+class ServiceReferenceDTOJsonDeserializer extends StdDeserializer<ServiceReferenceDTO> {
+
+    protected ServiceReferenceDTOJsonDeserializer() {
+        this(null);
+    }
+
+    protected ServiceReferenceDTOJsonDeserializer(Class<ServiceReferenceDTO> t) {
+        super(t);
+    }
+
+    @Override
+    public ServiceReferenceDTO deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException {
+
+        ServiceReferenceDTO referenceDTO = new ServiceReferenceDTO();
+        JsonNode node = jp.getCodec().readTree(jp);
+        referenceDTO.id = node.get("id").asLong();
+        referenceDTO.properties = new HashMap<>();
+        node.get("properties").fields().forEachRemaining(
+            entry -> {
+                referenceDTO.properties.put(entry.getKey(), Coerce.from(entry));
+            }
+        );
+
+        String bundleURL = node.get("bundle").asText();
+        referenceDTO.bundle = Long.parseLong(bundleURL.substring(bundleURL.lastIndexOf('/') + 1));
+
+        List<Long> usingBundles = new ArrayList<>();
+        for (JsonNode arrayNode : (ArrayNode)node.get("usingBundles")) {
+            bundleURL = arrayNode.asText();
+            usingBundles.add(Long.parseLong(bundleURL.substring(bundleURL.lastIndexOf('/') + 1)));
+        }
+
+        referenceDTO.usingBundles = usingBundles.stream().mapToLong(Long::longValue).toArray();
+        return referenceDTO;
+    }
+
+}
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOJsonSerializer.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOJsonSerializer.java
new file mode 100644
index 0000000..90faa6a
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOJsonSerializer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import java.io.IOException;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.osgi.framework.dto.ServiceReferenceDTO;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+@SuppressWarnings("serial")
+class ServiceReferenceDTOJsonSerializer extends StdSerializer<ServiceReferenceDTO> {
+
+    private final UriInfo uriInfo;
+
+    protected ServiceReferenceDTOJsonSerializer(UriInfo uriInfo) {
+        this(uriInfo, null);
+    }
+
+    protected ServiceReferenceDTOJsonSerializer(UriInfo uriInfo, Class<ServiceReferenceDTO> t) {
+        super(t);
+        this.uriInfo = uriInfo;
+    }
+
+    @Override
+    public void serialize(
+            ServiceReferenceDTO value, JsonGenerator gen, SerializerProvider provider)
+        throws IOException {
+
+        gen.writeStartObject();
+        gen.writeNumberField("id", value.id);
+        gen.writeObjectField("properties", value.properties);
+        gen.writeStringField(
+            "bundle",
+            uriInfo.getBaseUriBuilder().path("framework").path("bundle").path("{id}").build(value.bundle).toASCIIString()
+        );
+        gen.writeArrayFieldStart("usingBundles");
+        for (long usingBundle : value.usingBundles) {
+            gen.writeString(
+                uriInfo.getBaseUriBuilder().path("framework").path("bundle").path("{id}").build(usingBundle).toASCIIString()
+            );
+        }
+        gen.writeEndArray();
+        gen.writeEndObject();
+    }
+
+}
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOXMLDeserializer.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOXMLDeserializer.java
new file mode 100644
index 0000000..583ab8d
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOXMLDeserializer.java
@@ -0,0 +1,132 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.XMLEvent;
+
+import org.osgi.framework.dto.ServiceReferenceDTO;
+
+class ServiceReferenceDTOXMLDeserializer {
+
+    private final XMLDeserializer deserializer;
+
+    public ServiceReferenceDTOXMLDeserializer(XMLDeserializer deserializer) {
+        this.deserializer = deserializer;
+    }
+
+    public ServiceReferenceDTO deserializeServiceReferenceDTO(InputStream entityStream) throws IOException {
+        AtomicReference<ServiceReferenceDTO> service = new AtomicReference<>();
+        deserialize(service::set, entityStream);
+        return service.get();
+    }
+
+    public List<ServiceReferenceDTO> deserializeServiceReferenceDTOList(InputStream entityStream) throws IOException {
+        List<ServiceReferenceDTO> services = new ArrayList<>();
+        deserialize(services::add, entityStream);
+        return services;
+    }
+
+    private void deserialize(
+            Consumer<ServiceReferenceDTO> collector, InputStream entityStream)
+        throws IOException {
+
+        AtomicReference<ServiceReferenceDTO> service = new AtomicReference<>();
+        AtomicBoolean inUsingBundles = new AtomicBoolean();
+        deserializer.deserialize((start, next) -> {
+            String el = start.getName().getLocalPart();
+            XMLEvent event = start;
+            if (el.equals("service")) {
+                service.set(new ServiceReferenceDTO());
+            }
+            else if (el.equals("id")) {
+                try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                service.get().id = Long.parseLong(event.asCharacters().getData());
+            }
+            else if (el.equals("properties")) {
+                service.get().properties = new HashMap<>();
+            }
+            else if (el.equals("property")) {
+                Attribute keyAT = start.getAttributeByName(new QName("name"));
+                Attribute typeAT = start.getAttributeByName(new QName("type"));
+                Attribute valueAT = start.getAttributeByName(new QName("value"));
+                String valueTxt = null;
+                boolean array = false;
+                if (valueAT == null) {
+                    try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                    if (!event.isEndElement()) {
+                        valueTxt = event.asCharacters().getData();
+                        array = true;
+                    }
+                }
+                else {
+                    valueTxt = valueAT.getValue();
+                }
+
+                service.get().properties.put(keyAT.getValue(), Coerce.from(keyAT, typeAT, valueTxt, array));
+            }
+            else if (el.equals("bundle") && inUsingBundles.get()) {
+                try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                long[] newArray = new long[service.get().usingBundles.length + 1];
+                System.arraycopy(service.get().usingBundles, 0, newArray, 0, service.get().usingBundles.length);
+                newArray[newArray.length - 1] = bundleIdFromURI(event.asCharacters().getData());
+                service.get().usingBundles = newArray;
+            }
+            else if (el.equals("bundle")) {
+                try {event = next.nextEvent();} catch (Exception e) {throw new RuntimeException(e);}
+                service.get().bundle = bundleIdFromURI(event.asCharacters().getData());
+            }
+            else if (el.equals("usingBundles")) {
+                service.get().usingBundles = new long[0];
+                inUsingBundles.set(true);
+            }
+            return event;
+        }, end -> {
+            String el = end.getName().getLocalPart();
+            if (el.equals("service")) {
+                collector.accept(service.get());
+                service.set(null);
+            }
+            else if (el.equals("usingBundles")) {
+                inUsingBundles.set(false);
+            }
+        }, entityStream);
+    }
+
+    long bundleIdFromURI(String bundleURI) {
+        final URI uri = URI.create(bundleURI);
+        final String uriPath = uri.getPath();
+        final Path path = Paths.get(uriPath);
+        final String bundleId = path.getFileName().toString();
+        return Long.parseLong(bundleId);
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOXMLSerializer.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOXMLSerializer.java
new file mode 100644
index 0000000..939707b
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/ServiceReferenceDTOXMLSerializer.java
@@ -0,0 +1,178 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import static java.util.Collections.singleton;
+import static java.util.stream.Collectors.joining;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.osgi.framework.dto.ServiceReferenceDTO;
+import org.w3c.dom.Element;
+
+class ServiceReferenceDTOXMLSerializer {
+
+    private final XMLSerializer serializer;
+
+    public ServiceReferenceDTOXMLSerializer(XMLSerializer serializer) {
+        this.serializer = serializer;
+    }
+
+    public void serialize(
+        ServiceReferenceDTO service, UriInfo uriInfo, OutputStream entityStream) throws IOException {
+
+        serialize(singleton(service), null, uriInfo, entityStream);
+    }
+
+
+    public void serialize(
+        Collection<ServiceReferenceDTO> services, UriInfo uriInfo, OutputStream entityStream) throws IOException {
+
+        serialize(services, "services", uriInfo, entityStream);
+    }
+
+    public void serialize(
+        Collection<ServiceReferenceDTO> services, String rootElement, UriInfo uriInfo, OutputStream entityStream) throws IOException {
+
+        try {
+            serializer.serialize(
+                rootElement,
+                (dom, parentNode) -> services.stream().map(
+                    serviceReferenceDTO -> {
+                        Element serviceEl = dom.createElement("service");
+
+                        Element idEl = dom.createElement("id");
+                        idEl.setTextContent(String.valueOf(serviceReferenceDTO.id));
+                        Element propertiesEl = dom.createElement("properties");
+                        serviceReferenceDTO.properties.forEach((k, v) -> {
+                            Element propertyEl = dom.createElement("property");
+                            propertyEl.setAttribute("name", k);
+                            boolean array = false;
+                            if (Long.class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Long");
+                            }
+                            else if (Long[].class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Long");
+                                array = true;
+                            }
+                            else if (Double.class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Double");
+                            }
+                            else if (Double[].class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Double");
+                                array = true;
+                            }
+                            else if (Float.class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Float");
+                            }
+                            else if (Float[].class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Float");
+                                array = true;
+                            }
+                            else if (Integer.class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Integer");
+                            }
+                            else if (Integer[].class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Integer");
+                                array = true;
+                            }
+                            else if (Byte.class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Byte");
+                            }
+                            else if (Byte[].class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Byte");
+                                array = true;
+                            }
+                            else if (Character.class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Character");
+                            }
+                            else if (Character[].class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Character");
+                                array = true;
+                            }
+                            else if (Boolean.class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Boolean");
+                            }
+                            else if (Boolean[].class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Boolean");
+                                array = true;
+                            }
+                            else if (Short.class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Short");
+                            }
+                            else if (Short[].class.isInstance(v)) {
+                                propertyEl.setAttribute("type", "Short");
+                                array = true;
+                            }
+                            else if (String[].class.isInstance(v)) {
+                                array = true;
+                            }
+
+                            if (!array) {
+                                propertyEl.setAttribute("value", String.valueOf(v));
+                            }
+                            else {
+                                propertyEl.setTextContent(
+                                    Arrays.stream((Object[])v).map(
+                                        String::valueOf
+                                    ).collect(
+                                        joining("\n")
+                                    )
+                                );
+                            }
+
+                            propertiesEl.appendChild(propertyEl);
+                        });
+
+                        Element bundleEl = dom.createElement("bundle");
+                        bundleEl.setTextContent(
+                            uriInfo.getBaseUriBuilder().path("framework").path("bundle").path("{id}").build(serviceReferenceDTO.bundle).toASCIIString()
+                        );
+                        Element usingBundlesEl = dom.createElement("usingBundles");
+                        for (long usingBundle : serviceReferenceDTO.usingBundles) {
+                            Element bEl = dom.createElement("bundle");
+                            bEl.setTextContent(
+                                uriInfo.getBaseUriBuilder().path("framework").path("bundle").path("{id}").build(usingBundle).toASCIIString()
+                            );
+                            usingBundlesEl.appendChild(bEl);
+                        }
+
+                        serviceEl.appendChild(idEl);
+                        serviceEl.appendChild(propertiesEl);
+                        serviceEl.appendChild(bundleEl);
+                        serviceEl.appendChild(usingBundlesEl);
+
+                        return serviceEl;
+                    }
+                ).forEach(
+                    bundleEL -> parentNode.appendChild(bundleEL)
+                ),
+                entityStream
+            );
+        }
+        catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/XMLDeserializer.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/XMLDeserializer.java
new file mode 100644
index 0000000..e3f803d
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/XMLDeserializer.java
@@ -0,0 +1,60 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+class XMLDeserializer {
+
+    private final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
+
+    public <T> void deserialize(
+            BiFunction<StartElement, XMLEventReader, XMLEvent> startConsumer,
+            Consumer<EndElement> endConsumer, InputStream entityStream)
+        throws IOException {
+
+        try {
+            XMLEventReader eventReader = inputFactory.createXMLEventReader(entityStream);
+
+            while (eventReader.hasNext()) {
+                XMLEvent event = eventReader.nextEvent();
+
+                if (event.isStartElement()) {
+                    event = startConsumer.apply(event.asStartElement(), eventReader);
+                }
+
+                if (event.isEndElement()) {
+                    endConsumer.accept(event.asEndElement());
+                }
+            }
+        } catch (XMLStreamException e) {
+            throw new IOException(e);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/XMLSerializer.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/XMLSerializer.java
new file mode 100644
index 0000000..1739236
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/XMLSerializer.java
@@ -0,0 +1,81 @@
+/*
+ * 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.aries.jax.rs.rest.management.handler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.function.BiConsumer;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+class XMLSerializer {
+
+    private final DocumentBuilder db;
+
+    public XMLSerializer() {
+        try {
+            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+            db = dbf.newDocumentBuilder();
+        }
+        catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void serialize(
+            String rootElement, BiConsumer<Document, Node> consumer,
+            OutputStream entityStream)
+        throws Exception {
+
+        try {
+            Document dom = db.newDocument();
+            Node parentNode = dom;
+            if (rootElement != null) {
+                parentNode = dom.createElement(rootElement);
+                dom.appendChild(parentNode);
+            }
+
+            consumer.accept(dom, parentNode);
+
+            Transformer tr = TransformerFactory.newInstance().newTransformer();
+            tr.setOutputProperty(OutputKeys.STANDALONE, "yes");
+            tr.setOutputProperty(OutputKeys.INDENT, "yes");
+            tr.setOutputProperty(OutputKeys.METHOD, "xml");
+            tr.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+            tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+            tr.transform(
+                new DOMSource(dom),
+                new StreamResult(entityStream)
+            );
+        }
+        catch (Exception e) {
+            throw new IOException(e);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/package-info.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/package-info.java
new file mode 100644
index 0000000..1bf0e1c
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/handler/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+@Export
+@Version("1.0.0")
+package org.apache.aries.jax.rs.rest.management.handler;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/FrameworkResource.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/FrameworkResource.java
new file mode 100644
index 0000000..faa8fb4
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/FrameworkResource.java
@@ -0,0 +1,694 @@
+/*
+ * 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.aries.jax.rs.rest.management.internal;
+
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
+import static java.util.stream.Collectors.toList;
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.*;
+import static org.osgi.framework.Bundle.ACTIVE;
+import static org.osgi.framework.Bundle.INSTALLED;
+import static org.osgi.framework.Bundle.RESOLVED;
+import static org.osgi.framework.Bundle.STARTING;
+import static org.osgi.framework.Bundle.STOPPING;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.time.Instant;
+import java.util.AbstractMap;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.aries.jax.rs.rest.management.model.BundleStateDTO;
+import org.apache.aries.jax.rs.rest.management.model.BundlesDTO;
+import org.apache.aries.jax.rs.rest.management.model.ServiceReferenceDTOs;
+import org.apache.aries.jax.rs.rest.management.model.ServicesDTO;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.framework.dto.FrameworkDTO;
+import org.osgi.framework.dto.ServiceReferenceDTO;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.framework.startlevel.dto.BundleStartLevelDTO;
+import org.osgi.framework.startlevel.dto.FrameworkStartLevelDTO;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+public class FrameworkResource {
+
+    private final BundleContext bundleContext;
+    private final Bundle framework;
+
+    @Context
+    volatile UriInfo uriInfo;
+    @Context
+    volatile HttpHeaders headers;
+
+    public FrameworkResource(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+        this.framework = bundleContext.getBundle(0);
+    }
+
+    @GET
+    @Path("framework{type: (\\.json|\\.xml)*}")
+    @Produces({APPLICATION_JSON, APPLICATION_XML})
+    public Response framework(@PathParam("type") String type) {
+        ResponseBuilder builder = Response.status(
+            Response.Status.OK
+        ).entity(
+            framework.adapt(FrameworkDTO.class)
+        );
+
+        return Optional.ofNullable(
+            type
+        ).map(
+            String::trim
+        ).map(
+            t -> ".json".equals(t) ? APPLICATION_JSON : APPLICATION_XML
+        ).map(t -> builder.type(t)).orElse(
+            builder
+        ).build();
+    }
+
+    @GET
+    @Produces({APPLICATION_BUNDLESTATE_JSON, APPLICATION_BUNDLESTATE_XML})
+    @Path("framework/state{type:(\\.json|\\.xml)*}")
+    public Response state(@PathParam("type") String type) {
+        ResponseBuilder builder = Response.status(
+            Response.Status.OK
+        ).entity(
+            state(framework)
+        );
+
+        return Optional.ofNullable(
+            type
+        ).map(
+            String::trim
+        ).map(
+            t -> ".json".equals(t) ? APPLICATION_BUNDLESTATE_JSON : APPLICATION_BUNDLESTATE_XML
+        ).map(t -> builder.type(t)).orElse(
+            builder
+        ).build();
+    }
+
+    // 137.3.1.1
+    @GET
+    @Produces({APPLICATION_FRAMEWORKSTARTLEVEL_JSON, APPLICATION_FRAMEWORKSTARTLEVEL_XML})
+    @Path("framework/startlevel{type:(\\.json|\\.xml)*}")
+    public Response startlevel(@PathParam("type") String type) {
+        ResponseBuilder builder = Response.status(
+            Response.Status.OK
+        ).entity(
+            framework.adapt(FrameworkStartLevelDTO.class)
+        );
+
+        return Optional.ofNullable(
+            type
+        ).map(
+            String::trim
+        ).map(
+            t -> ".json".equals(t) ? APPLICATION_FRAMEWORKSTARTLEVEL_JSON : APPLICATION_FRAMEWORKSTARTLEVEL_XML
+        ).map(t -> builder.type(t)).orElse(
+            builder
+        ).build();
+    }
+
+    // 137.3.1.2
+    @PUT
+    @Consumes({APPLICATION_FRAMEWORKSTARTLEVEL_JSON, APPLICATION_FRAMEWORKSTARTLEVEL_XML})
+    @Path("framework/startlevel")
+    public Response startlevel(FrameworkStartLevelDTO update) {
+        try {
+            FrameworkStartLevel current = framework.adapt(FrameworkStartLevel.class);
+
+            if (current.getStartLevel() != update.startLevel) {
+                current.setStartLevel(update.startLevel);
+            }
+            if (current.getInitialBundleStartLevel() != update.initialBundleStartLevel) {
+                current.setInitialBundleStartLevel(update.initialBundleStartLevel);
+            }
+
+            return Response.noContent().build();
+        }
+        catch (IllegalArgumentException exception) {
+            throw new WebApplicationException(exception, 400);
+        }
+    }
+
+    // 137.3.2.1
+    @GET
+    @Produces({APPLICATION_BUNDLES_JSON, APPLICATION_BUNDLES_XML})
+    @Path("framework/bundles")
+    public BundlesDTO bundles(@Context UriInfo info) {
+        Predicate<Bundle> predicate = fromNamespaceQuery(info);
+
+        return BundlesDTO.build(
+            Stream.of(
+                bundleContext.getBundles()
+            ).filter(
+                predicate
+            ).map(
+                Bundle::getBundleId
+            ).map(
+                String::valueOf
+            ).map(
+                id -> uriInfo.getBaseUriBuilder().path("framework").path("bundle").path("{id}").build(id).toASCIIString()
+            ).collect(toList())
+        );
+    }
+
+    // 137.3.2.2
+    @POST
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces({APPLICATION_BUNDLE_JSON, APPLICATION_BUNDLE_XML})
+    @Path("framework/bundles")
+    public Response postBundles(String location) {
+        try {
+            Instant now = Instant.now().minusMillis(2);
+            Bundle bundle = bundleContext.installBundle(location);
+
+            if (!now.isBefore(Instant.ofEpochMilli(bundle.getLastModified()))) {
+                throw new WebApplicationException(location, 409);
+            }
+
+            return Response.ok(
+                bundle.adapt(BundleDTO.class)
+            ).build();
+        }
+        catch (BundleException exception) {
+            throw new WebApplicationException(exception, 400);
+        }
+    }
+
+    // 137.3.2.3
+    @POST
+    @Consumes(APPLICATION_VNDOSGIBUNDLE)
+    @Produces({APPLICATION_BUNDLE_JSON, APPLICATION_BUNDLE_XML})
+    @Path("framework/bundles")
+    public Response postBundles(
+        InputStream inputStream,
+        @HeaderParam(HttpHeaders.CONTENT_LOCATION) String location) {
+
+        try {
+            location = Optional.ofNullable(location).orElseGet(
+                () -> "org.apache.aries.jax.rs.whiteboard:".concat(
+                    UUID.randomUUID().toString()));
+
+            Instant now = Instant.now().minusMillis(2);
+            Bundle bundle = bundleContext.installBundle(location, inputStream);
+
+            if (!now.isBefore(Instant.ofEpochMilli(bundle.getLastModified()))) {
+                throw new WebApplicationException(location, 409);
+            }
+
+            return Response.ok(
+                bundle.adapt(BundleDTO.class)
+            ).build();
+        }
+        catch (BundleException exception) {
+            throw new WebApplicationException(exception, 400);
+        }
+    }
+
+    // 137.3.3.1
+    @GET
+    @Produces({APPLICATION_BUNDLES_REPRESENTATIONS_JSON, APPLICATION_BUNDLES_REPRESENTATIONS_XML})
+    @Path("framework/bundles/representations")
+    public Collection<BundleDTO> bundleDTOs(@Context UriInfo info) {
+        Predicate<Bundle> predicate = fromNamespaceQuery(info);
+
+        return Stream.of(
+            bundleContext.getBundles()
+        ).filter(
+            predicate
+        ).map(
+            b -> b.adapt(BundleDTO.class)
+        ).collect(Collectors.toList());
+    }
+
+    // 137.3.4.1
+    @GET
+    @Produces({APPLICATION_BUNDLE_JSON, APPLICATION_BUNDLE_XML})
+    @Path("framework/bundle/{bundleid}")
+    public BundleDTO bundle(@PathParam("bundleid") long bundleid) {
+        Bundle bundle = bundleContext.getBundle(bundleid);
+
+        if (bundle == null) {
+            throw new WebApplicationException(404);
+        }
+
+        return bundle.adapt(BundleDTO.class);
+    }
+
+    // 137.3.4.2
+    @PUT
+    @Consumes(MediaType.TEXT_PLAIN)
+    @Produces({APPLICATION_BUNDLE_JSON, APPLICATION_BUNDLE_XML})
+    @Path("framework/bundle/{bundleid}")
+    public Response postBundle(
+        @PathParam("bundleid") long bundleid, String location) {
+
+        try {
+            Bundle bundle = bundleContext.getBundle(bundleid);
+
+            if (bundle == null) {
+                throw new WebApplicationException(String.valueOf(bundleid), 404);
+            }
+
+            if (location != null && !location.isEmpty()) {
+                bundle.update(new URL(location).openStream());
+            }
+            else {
+                bundle.update();
+            }
+
+            return Response.ok(
+                bundle.adapt(BundleDTO.class)
+            ).build();
+        }
+        catch (Exception exception) {
+            throw new WebApplicationException(exception, 400);
+        }
+    }
+
+    // 137.3.4.3
+    @PUT
+    @Consumes(APPLICATION_VNDOSGIBUNDLE)
+    @Produces({APPLICATION_BUNDLE_JSON, APPLICATION_BUNDLE_XML})
+    @Path("framework/bundle/{bundleid}")
+    public Response postBundle(
+        @PathParam("bundleid") long bundleid, InputStream inputStream) {
+
+        try {
+            Bundle bundle = bundleContext.getBundle(bundleid);
+
+            if (bundle == null) {
+                throw new WebApplicationException(String.valueOf(bundleid), 404);
+            }
+
+            bundle.update(inputStream);
+
+            return Response.ok(
+                bundle.adapt(BundleDTO.class)
+            ).build();
+        }
+        catch (Exception exception) {
+            throw new WebApplicationException(exception, 400);
+        }
+    }
+
+    // 137.3.4.4
+    @DELETE
+    @Produces({APPLICATION_BUNDLE_JSON, APPLICATION_BUNDLE_XML})
+    @Path("framework/bundle/{bundleid}")
+    public Response deleteBundle(@PathParam("bundleid") long bundleid) {
+
+        try {
+            Bundle bundle = bundleContext.getBundle(bundleid);
+
+            if (bundle == null) {
+                throw new WebApplicationException(String.valueOf(bundleid), 404);
+            }
+
+            bundle.uninstall();
+
+            return Response.ok(
+                bundle.adapt(BundleDTO.class)
+            ).build();
+        }
+        catch (Exception exception) {
+            throw new WebApplicationException(exception, 400);
+        }
+    }
+
+    // 137.3.5.1
+    @GET
+    @Produces({APPLICATION_BUNDLESTATE_JSON, APPLICATION_BUNDLESTATE_XML})
+    @Path("framework/bundle/{bundleid}/state")
+    public BundleStateDTO bundleState(@PathParam("bundleid") long bundleid) {
+        Bundle bundle = bundleContext.getBundle(bundleid);
+
+        if (bundle == null) {
+            throw new WebApplicationException(404);
+        }
+
+        return state(bundle);
+    }
+
+    // 137.3.5.2
+    @PUT
+    @Consumes({APPLICATION_BUNDLESTATE_JSON, APPLICATION_BUNDLESTATE_XML})
+    @Produces({APPLICATION_BUNDLESTATE_JSON, APPLICATION_BUNDLESTATE_XML})
+    @Path("framework/bundle/{bundleid}/state")
+    public BundleStateDTO bundleState(
+        @PathParam("bundleid") long bundleid,
+        BundleStateDTO bundleStateDTO) {
+
+        Bundle bundle = bundleContext.getBundle(bundleid);
+
+        if (bundle == null) {
+            throw new WebApplicationException(404);
+        }
+
+        int currentState = bundle.getState();
+
+        try {
+            if ((currentState & INSTALLED) == INSTALLED ||
+                (currentState & RESOLVED) == RESOLVED) {
+                if ((bundleStateDTO.state & ACTIVE) == ACTIVE) {
+                    bundle.start(bundleStateDTO.options);
+                }
+                else if ((bundleStateDTO.state & RESOLVED) == RESOLVED) {
+                    framework.adapt(
+                        FrameworkWiring.class
+                    ).resolveBundles(
+                        Collections.singleton(bundle)
+                    );
+                }
+            }
+            else if ((currentState & ACTIVE) == ACTIVE) {
+                if ((bundleStateDTO.state & RESOLVED) == RESOLVED) {
+                    bundle.stop(bundleStateDTO.options);
+                }
+            }
+            else {
+                throw new WebApplicationException(
+                    String.format(
+                        "the requested target state [%s] is not reachable " +
+                        "from the current bundle state [%s] or is not a " +
+                        "target state",
+                        bundleStateDTO.state, currentState), 402);
+            }
+        }
+        catch (BundleException exception) {
+            throw new WebApplicationException(exception, 400);
+        }
+
+        return state(bundle);
+    }
+
+    // 137.3.6.1
+    @GET
+    @Produces({APPLICATION_BUNDLEHEADER_JSON, APPLICATION_BUNDLEHEADER_XML})
+    @Path("framework/bundle/{bundleid}/header")
+    public Dictionary<String, String> bundleHeaders(
+        @PathParam("bundleid") long bundleid) {
+
+        Bundle bundle = bundleContext.getBundle(bundleid);
+
+        if (bundle == null) {
+            throw new WebApplicationException(404);
+        }
+
+        return bundle.getHeaders();
+    }
+
+    // 137.3.7.1
+    @GET
+    @Produces({APPLICATION_BUNDLESTARTLEVEL_JSON, APPLICATION_BUNDLESTARTLEVEL_XML})
+    @Path("framework/bundle/{bundleid}/startlevel")
+    public BundleStartLevelDTO bundleStartlevel(
+        @PathParam("bundleid") long bundleid) {
+
+        Bundle bundle = bundleContext.getBundle(bundleid);
+
+        if (bundle == null) {
+            throw new WebApplicationException(404);
+        }
+
+        return bundle.adapt(BundleStartLevelDTO.class);
+    }
+
+    // 137.3.7.2
+    @PUT
+    @Consumes({APPLICATION_BUNDLESTARTLEVEL_JSON, APPLICATION_BUNDLESTARTLEVEL_XML})
+    @Produces({APPLICATION_BUNDLESTARTLEVEL_JSON, APPLICATION_BUNDLESTARTLEVEL_XML})
+    @Path("framework/bundle/{bundleid}/startlevel")
+    public Response bundleStartlevel(
+        @PathParam("bundleid") long bundleid, BundleStartLevelDTO update) {
+
+        Bundle bundle = bundleContext.getBundle(bundleid);
+
+        if (bundle == null) {
+            throw new WebApplicationException(404);
+        }
+
+        try {
+            BundleStartLevel current = bundle.adapt(BundleStartLevel.class);
+
+            if (current.getStartLevel() != update.startLevel) {
+                current.setStartLevel(update.startLevel);
+            }
+
+            return Response.ok(bundle.adapt(BundleStartLevelDTO.class)).build();
+        }
+        catch (IllegalArgumentException exception) {
+            throw new WebApplicationException(exception, 400);
+        }
+    }
+
+    // 137.3.8.1
+    @GET
+    @Produces({APPLICATION_SERVICES_JSON, APPLICATION_SERVICES_XML})
+    @Path("framework/services")
+    public ServicesDTO services(@QueryParam("filter") String filter) {
+        Predicate<ServiceReferenceDTO> predicate = fromFilterQuery(filter);
+
+        return ServicesDTO.build(
+            framework.adapt(FrameworkDTO.class).services.stream().filter(
+                predicate
+            ).map(
+                sr -> String.valueOf(sr.id)
+            ).map(
+                id -> uriInfo.getBaseUriBuilder().path("framework").path("service").path(id)
+            ).map(
+                UriBuilder::build
+            ).map(
+                URI::toASCIIString
+            )
+            .collect(toList())
+        );
+    }
+
+    // 137.3.9.1
+    @GET
+    @Produces({APPLICATION_SERVICES_REPRESENTATIONS_JSON, APPLICATION_SERVICES_REPRESENTATIONS_XML})
+    @Path("framework/services/representations")
+    public ServiceReferenceDTOs serviceDTOs(@QueryParam("filter") String filter) {
+        Predicate<ServiceReferenceDTO> predicate = fromFilterQuery(filter);
+
+        return ServiceReferenceDTOs.build(
+            framework.adapt(
+                FrameworkDTO.class
+            ).services.stream().filter(
+                predicate
+            ).collect(toList())
+        );
+    }
+
+    // 137.3.9.1
+    @GET
+    @Produces({APPLICATION_SERVICE_JSON, APPLICATION_SERVICE_XML})
+    @Path("framework/service/{serviceid}")
+    public ServiceReferenceDTO service(
+        @PathParam("serviceid") long serviceid) {
+
+        return Stream.of(
+            bundleContext.getBundles()
+        ).flatMap(
+            bundle -> Optional.ofNullable(
+                bundle.adapt(ServiceReferenceDTO[].class)
+            ).map(
+                Stream::of
+            ).orElseGet(
+                Stream::empty
+            )
+        ).filter(
+            sr -> sr.id == serviceid
+        ).findFirst().orElseThrow(
+            () -> new WebApplicationException(404)
+        );
+    }
+
+    private BundleStateDTO state(Bundle bundle) {
+        final BundleStateDTO bundleState = new BundleStateDTO();
+        bundleState.state = bundle.getState();
+        BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class);
+        bundleState.options =
+            (bundleStartLevel.isActivationPolicyUsed() ? Bundle.START_ACTIVATION_POLICY : 0) |
+            (((bundleState.state & (STARTING|ACTIVE|STOPPING)) != 0) ?
+                (bundleStartLevel.isPersistentlyStarted() ? 0 : Bundle.START_TRANSIENT) : 0);
+        return bundleState;
+    }
+
+    static Predicate<Bundle> fromNamespaceQuery(UriInfo info) {
+        MultivaluedMap<String,String> parameters = info.getQueryParameters(true);
+
+        if (parameters.isEmpty()) {
+            return b -> true;
+        }
+
+        Map<String, List<Filter>> filters = parameters.entrySet().stream().flatMap(
+            e -> e.getValue().stream().map(v -> new SimpleEntry<>(e.getKey(), v))
+        ).map(
+            e -> new SimpleEntry<>(e.getKey(), create(e.getValue()))
+        ).collect(
+            groupingBy(Entry::getKey, mapping(Entry::getValue, toList()))
+        );
+
+        if (filters.isEmpty()) {
+            return b -> true;
+        }
+
+        return b -> {
+            BundleWiring wiring = b.adapt(BundleWiring.class);
+            return filters.entrySet().stream().allMatch(
+                entry -> {
+                    String namespace = entry.getKey();
+                    List<BundleCapability> caps = wiring.getCapabilities(namespace);
+                    return entry.getValue().stream().anyMatch(
+                        f ->
+                            caps.stream().anyMatch(
+                                cap ->
+                                    f.matches(cap.getAttributes())
+                            )
+                    );
+                }
+            );
+        };
+    }
+
+    static Predicate<ServiceReferenceDTO> fromFilterQuery(String filter) {
+        return Optional.ofNullable(
+            filter
+        ).map(
+            FrameworkResource::create
+        ).map(
+            toPredicate()
+        ).orElseGet(
+            () -> sr -> true
+        );
+    }
+
+    static Filter create(String filter) {
+        try {
+            return FrameworkUtil.createFilter(filter);
+        } catch (InvalidSyntaxException e) {
+            throw new WebApplicationException(
+                String.format("Malformed filter [%s]", filter),
+                Response.Status.BAD_REQUEST);
+        }
+    }
+
+    static Function<Filter, Predicate<ServiceReferenceDTO>> toPredicate() {
+        return f -> sr -> f.matches(new CaseInsensitiveMap(sr.properties));
+    }
+
+
+    /**
+     * This Map is used for case-insensitive key lookup during filter
+     * evaluation. This Map implementation only supports the get operation using
+     * a String key as no other operations are used by the Filter
+     * implementation.
+     */
+    private static final class CaseInsensitiveMap extends AbstractMap<String, Object> {
+        private final String[] keys;
+        private final Map<String, Object> properties;
+
+        /**
+         * Create a case insensitive map from the specified dictionary.
+         *
+         * @param properties
+         * @throws IllegalArgumentException If {@code dictionary} contains case
+         *             variants of the same key name.
+         */
+        CaseInsensitiveMap(Map<String, Object> properties) {
+            this.properties = properties;
+            List<String> keyList = new ArrayList<>(this.properties.size());
+            this.properties.forEach((k,v) -> {
+                for (String i : keyList) {
+                    if (k.equalsIgnoreCase(i)) {
+                        throw new IllegalArgumentException();
+                    }
+                }
+                keyList.add(k);
+            });
+            this.keys = keyList.toArray(new String[0]);
+        }
+
+        @Override
+        public Object get(Object o) {
+            String k = (String) o;
+            for (String key : keys) {
+                if (key.equalsIgnoreCase(k)) {
+                    return super.get(key);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public Set<Entry<String, Object>> entrySet() {
+            return this.properties.entrySet();
+        }
+
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestClientFactoryImpl.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestClientFactoryImpl.java
new file mode 100644
index 0000000..9ef5bd4
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestClientFactoryImpl.java
@@ -0,0 +1,43 @@
+/*
+ * 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.aries.jax.rs.rest.management.internal;
+
+import java.net.URI;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+
+import org.osgi.service.rest.client.RestClient;
+import org.osgi.service.rest.client.RestClientFactory;
+
+public class RestClientFactoryImpl implements RestClientFactory {
+
+    private final ClientBuilder clientBuilder;
+
+    public RestClientFactoryImpl(ClientBuilder clientBuilder) {
+        this.clientBuilder = clientBuilder;
+    }
+
+    @Override
+    public RestClient createRestClient(URI uri) {
+        Client client = clientBuilder.build();
+
+        return new RestClientImpl(client.target(uri));
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestClientImpl.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestClientImpl.java
new file mode 100644
index 0000000..d243dd5
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestClientImpl.java
@@ -0,0 +1,507 @@
+/*
+ * 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.aries.jax.rs.rest.management.internal;
+
+import static javax.ws.rs.core.MediaType.*;
+import static org.apache.aries.jax.rs.rest.management.RestManagementConstants.*;
+
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.StatusType;
+
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.apache.aries.jax.rs.rest.management.model.BundleStateDTO;
+import org.apache.aries.jax.rs.rest.management.model.BundlesDTO;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.dto.BundleDTO;
+import org.osgi.framework.dto.ServiceReferenceDTO;
+import org.osgi.framework.startlevel.dto.BundleStartLevelDTO;
+import org.osgi.framework.startlevel.dto.FrameworkStartLevelDTO;
+import org.osgi.service.rest.client.RestClient;
+
+public class RestClientImpl implements RestClient {
+
+    private static final GenericType<List<BundleDTO>> BUNDLE_LIST =
+        new GenericType<List<BundleDTO>>() {};
+
+    private final WebTarget webTarget;
+
+    public RestClientImpl(WebTarget webTarget) {
+        this.webTarget = webTarget;
+
+        this.webTarget.register(RestManagementMessageBodyHandler.class);
+    }
+
+    @Override
+    public FrameworkStartLevelDTO getFrameworkStartLevel() throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "startlevel"
+            ).request().get()
+        )) {
+            return response.readEntity(FrameworkStartLevelDTO.class);
+        }
+    }
+
+    @Override
+    public void setFrameworkStartLevel(FrameworkStartLevelDTO startLevel) throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "startlevel"
+            ).request().put(
+                Entity.entity(
+                    startLevel, APPLICATION_FRAMEWORKSTARTLEVEL_JSON)
+            )
+        )) {}
+    }
+
+    @Override
+    public Collection<String> getBundlePaths() throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "bundles"
+            ).request().get()
+        )) {
+            return response.readEntity(BundlesDTO.class).bundles;
+        }
+    }
+
+    @Override
+    public Collection<BundleDTO> getBundles() throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "bundles"
+            ).path(
+                "representations"
+            ).request().get()
+        )) {
+            return response.readEntity(BUNDLE_LIST);
+        }
+    }
+
+    @Override
+    public BundleDTO getBundle(long id) throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "bundle"
+            ).path(
+                "{bundleid}"
+            ).resolveTemplate(
+                "bundleid", id
+            ).request().get()
+        )) {
+            return response.readEntity(BundleDTO.class);
+        }
+    }
+
+    @Override
+    public BundleDTO getBundle(String bundlePath) throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                bundlePath
+            ).request().get()
+        )) {
+            return response.readEntity(BundleDTO.class);
+        }
+    }
+
+    @Override
+    public int getBundleState(long id) throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "bundle"
+            ).path(
+                "{bundleid}"
+            ).path(
+                "state"
+            ).resolveTemplate(
+                "bundleid", id
+            ).request().get()
+        )) {
+            return response.readEntity(BundleStateDTO.class).state;
+        }
+    }
+
+    @Override
+    public int getBundleState(String bundlePath) throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                bundlePath
+            ).path(
+                "state"
+            ).request().get()
+        )) {
+            return response.readEntity(BundleStateDTO.class).state;
+        }
+    }
+
+    @Override
+    public void startBundle(long id) throws Exception {
+        BundleStateDTO bundleStateDTO = new BundleStateDTO();
+        bundleStateDTO.state = Bundle.ACTIVE;
+
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "bundle"
+            ).path(
+                "{bundleid}"
+            ).path(
+                "state"
+            ).resolveTemplate(
+                "bundleid", id
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            )
+        )) {
+        }
+    }
+
+    @Override
+    public void startBundle(String bundlePath) throws Exception {
+        BundleStateDTO bundleStateDTO = new BundleStateDTO();
+        bundleStateDTO.state = Bundle.ACTIVE;
+
+        try (Response response = maybeThrow(
+            webTarget.path(
+                bundlePath
+            ).path(
+                "state"
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            )
+        )) {
+        }
+    }
+
+    @Override
+    public void startBundle(long id, int options) throws Exception {
+        BundleStateDTO bundleStateDTO = new BundleStateDTO();
+        bundleStateDTO.state = Bundle.ACTIVE;
+        bundleStateDTO.options = options;
+
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "bundle"
+            ).path(
+                "{bundleid}"
+            ).path(
+                "state"
+            ).resolveTemplate(
+                "bundleid", id
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            )
+        )) {
+        }
+    }
+
+    @Override
+    public void startBundle(String bundlePath, int options) throws Exception {
+        BundleStateDTO bundleStateDTO = new BundleStateDTO();
+        bundleStateDTO.state = Bundle.ACTIVE;
+        bundleStateDTO.options = options;
+
+        try (Response response = maybeThrow(
+            webTarget.path(
+                bundlePath
+            ).path(
+                "state"
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            )
+        )) {
+        }
+    }
+
+    @Override
+    public void stopBundle(long id) throws Exception {
+        BundleStateDTO bundleStateDTO = new BundleStateDTO();
+        bundleStateDTO.state = Bundle.RESOLVED;
+
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "bundle"
+            ).path(
+                "{bundleid}"
+            ).path(
+                "state"
+            ).resolveTemplate(
+                "bundleid", id
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            )
+        )) {
+        }
+    }
+
+    @Override
+    public void stopBundle(String bundlePath) throws Exception {
+        BundleStateDTO bundleStateDTO = new BundleStateDTO();
+        bundleStateDTO.state = Bundle.RESOLVED;
+
+        try (Response response = maybeThrow(
+            webTarget.path(
+                bundlePath
+            ).path(
+                "state"
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            )
+        )) {
+        }
+    }
+
+    @Override
+    public void stopBundle(long id, int options) throws Exception {
+        BundleStateDTO bundleStateDTO = new BundleStateDTO();
+        bundleStateDTO.state = Bundle.RESOLVED;
+        bundleStateDTO.options = options;
+
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework"
+            ).path(
+                "bundle"
+            ).path(
+                "{bundleid}"
+            ).path(
+                "state"
+            ).resolveTemplate(
+                "bundleid", id
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            )
+        )) {
+        }
+    }
+
+    @Override
+    public void stopBundle(String bundlePath, int options) throws Exception {
+        BundleStateDTO bundleStateDTO = new BundleStateDTO();
+        bundleStateDTO.state = Bundle.RESOLVED;
+        bundleStateDTO.options = options;
+
+        try (Response response = maybeThrow(
+            webTarget.path(
+                bundlePath
+            ).path(
+                "state"
+            ).request().put(
+                Entity.entity(
+                    bundleStateDTO,
+                    APPLICATION_BUNDLESTATE_JSON_TYPE
+                )
+            )
+        )) {
+        }
+    }
+
+    @Override
+    public Map<String, String> getBundleHeaders(long id) throws Exception {
+//        try (Response response = maybeThrow(
+//            webTarget.path(
+//                "framework"
+//            ).path(
+//                "bundle"
+//            ).path(
+//                "{bundleid}"
+//            ).path(
+//                "header"
+//            ).resolveTemplate(
+//                    "bundleid", id
+//            ).request().get()
+//        )) {
+//        }
+        // TODO
+        return null;
+    }
+
+    @Override
+    public Map<String, String> getBundleHeaders(String bundlePath) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public BundleStartLevelDTO getBundleStartLevel(long id) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public BundleStartLevelDTO getBundleStartLevel(String bundlePath) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public void setBundleStartLevel(long id, int startLevel) throws Exception {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void setBundleStartLevel(String bundlePath, int startLevel) throws Exception {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public BundleDTO installBundle(String location) throws Exception {
+        try (Response response = maybeThrow(
+            webTarget.path(
+                "framework/bundles"
+            ).request(APPLICATION_BUNDLE_JSON).post(
+                Entity.entity(location, TEXT_PLAIN)
+            )
+        )) {
+            return response.readEntity(
+                BundleDTO.class
+            );
+        }
+    }
+
+    @Override
+    public BundleDTO installBundle(String location, InputStream in) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public BundleDTO uninstallBundle(long id) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public BundleDTO uninstallBundle(String bundlePath) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public BundleDTO updateBundle(long id) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public BundleDTO updateBundle(long id, String url) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public BundleDTO updateBundle(long id, InputStream in) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Collection<String> getServicePaths() throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Collection<String> getServicePaths(String filter) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Collection<ServiceReferenceDTO> getServiceReferences() throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public Collection<ServiceReferenceDTO> getServiceReferences(String filter) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public ServiceReferenceDTO getServiceReference(long id) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public ServiceReferenceDTO getServiceReference(String servicePath) throws Exception {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    private Response maybeThrow(Response response) throws Exception {
+        StatusType statusInfo = response.getStatusInfo();
+
+        if (statusInfo.getStatusCode() >= 300) {
+            throw new Exception(statusInfo.getReasonPhrase());
+        }
+
+        return response;
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestManagementActivator.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestManagementActivator.java
new file mode 100644
index 0000000..0b7cccc
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestManagementActivator.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.aries.jax.rs.rest.management.internal;
+
+import static org.apache.aries.component.dsl.OSGi.all;
+import static org.apache.aries.component.dsl.OSGi.ignore;
+import static org.apache.aries.component.dsl.OSGi.register;
+import static org.apache.aries.component.dsl.OSGi.service;
+import static org.apache.aries.component.dsl.OSGi.serviceReferences;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_APPLICATION_SELECT;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_EXTENSION;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_EXTENSION_SELECT;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_NAME;
+import static org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants.JAX_RS_RESOURCE;
+
+import java.util.HashMap;
+import java.util.function.BiFunction;
+
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.ext.MessageBodyReader;
+import javax.ws.rs.ext.MessageBodyWriter;
+
+import org.apache.aries.component.dsl.OSGiResult;
+import org.apache.aries.jax.rs.rest.management.handler.RestManagementMessageBodyHandler;
+import org.osgi.annotation.bundle.Header;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.PrototypeServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.jaxrs.whiteboard.JaxrsWhiteboardConstants;
+import org.osgi.service.rest.client.RestClientFactory;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Contact;
+import io.swagger.v3.oas.models.info.Info;
+
+@Header(name = Constants.BUNDLE_ACTIVATOR, value = "${@class}")
+public class RestManagementActivator implements BundleActivator {
+
+    public static final String RMS_BASE = "/rms";
+
+    private OSGiResult result;
+
+    @Override
+    public void start(BundleContext bundleContext) throws Exception {
+        result = all(
+            ignore(
+                register(
+                    Application.class,
+                    () -> new RestManagementApplication(),
+                    () -> {
+                        HashMap<String, Object> map = new HashMap<>();
+
+                        map.put(JAX_RS_NAME, RestManagementApplication.class.getSimpleName());
+                        map.put(
+                            JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE, RMS_BASE);
+
+                        return map;
+                    }
+                )
+            ),
+            ignore(
+                service(
+                    serviceReferences(ClientBuilder.class)
+                ).flatMap(
+                    clientBuilder -> register(
+                        RestClientFactory.class,
+                        new RestClientFactoryImpl(clientBuilder),
+                        null
+                    )
+                )
+            ),
+            ignore(
+                register(
+                    new String[] {
+                        MessageBodyReader.class.getName(),
+                        MessageBodyWriter.class.getName()
+                    },
+                    () -> new PrototypeWrapper<>(
+                        (b, r) -> new RestManagementMessageBodyHandler()
+                    ),
+                    () -> {
+                        HashMap<String, Object> map = new HashMap<>();
+
+                        map.put(JAX_RS_NAME, RestManagementMessageBodyHandler.class.getSimpleName());
+                        map.put(
+                            JAX_RS_APPLICATION_SELECT,
+                            String.format(
+                                "(%s=%s)", JAX_RS_NAME,
+                                RestManagementApplication.class.getSimpleName()));
+                        map.put(JAX_RS_EXTENSION, true);
+
+                        return map;
+                    }
+                )
+            ),
+            ignore(
+                register(
+                    FrameworkResource.class,
+                    () -> new FrameworkResource(bundleContext),
+                    () -> {
+                        HashMap<String, Object> map = new HashMap<>();
+
+                        map.put(
+                            JAX_RS_APPLICATION_SELECT,
+                            String.format(
+                                "(%s=%s)", JAX_RS_NAME,
+                                RestManagementApplication.class.getSimpleName()));
+                        map.put(JAX_RS_RESOURCE, true);
+                        map.put(JAX_RS_EXTENSION_SELECT, new String[] {
+                            String.format(
+                                "(%s=%s)", JAX_RS_NAME,
+                                RestManagementMessageBodyHandler.class.getSimpleName())
+                        });
+
+                        return map;
+                    }
+                )
+            ),
+            ignore(
+                register(
+                    OpenAPI.class,
+                    () -> {
+                        OpenAPI openAPI = new OpenAPI();
+
+                        openAPI.info(
+                            new Info()
+                                .title("Apache Aries OSGi Rest Management Service")
+                                .description("Apache Aries OSGi Rest Management Service REST API")
+                                .contact(
+                                    new Contact()
+                                        .email("dev@aries.apache.org"))
+                        );
+
+                        return openAPI;
+                    },
+                    () -> {
+                        HashMap<String, Object> map = new HashMap<>();
+
+                        map.put(
+                            JAX_RS_APPLICATION_SELECT,
+                            String.format(
+                                "(%s=%s)", JAX_RS_NAME,
+                                RestManagementApplication.class.getSimpleName()));
+
+                        return map;
+                    }
+                )
+            )
+        ).run(bundleContext);
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        result.close();
+    }
+
+    class PrototypeWrapper<S> implements PrototypeServiceFactory<S> {
+
+        private final BiFunction<Bundle, ServiceRegistration<S>, S> function;
+
+        public PrototypeWrapper(BiFunction<Bundle, ServiceRegistration<S>, S> function) {
+            this.function = function;
+        }
+
+        @Override
+        public S getService(Bundle bundle, ServiceRegistration<S> registration) {
+            return function.apply(bundle, registration);
+        }
+
+        @Override
+        public void ungetService(Bundle bundle, ServiceRegistration<S> registration, S service) {
+        }
+
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestManagementApplication.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestManagementApplication.java
new file mode 100644
index 0000000..7899130
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/RestManagementApplication.java
@@ -0,0 +1,23 @@
+/*
+ * 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.aries.jax.rs.rest.management.internal;
+
+import javax.ws.rs.core.Application;
+
+public class RestManagementApplication extends Application {
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/package-info.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/package-info.java
new file mode 100644
index 0000000..6100eb1
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/internal/package-info.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.
+ */
+
+@Requirement(
+    namespace = IMPLEMENTATION_NAMESPACE,
+    name = "org.apache.aries.jax.rs.openapi"
+)
+@Version("1.0.0")
+package org.apache.aries.jax.rs.rest.management.internal;
+
+import static org.osgi.namespace.implementation.ImplementationNamespace.IMPLEMENTATION_NAMESPACE;
+
+import org.osgi.annotation.bundle.Requirement;
+import org.osgi.annotation.versioning.Version;
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/BundleStateDTO.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/BundleStateDTO.java
new file mode 100644
index 0000000..8ec7895
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/BundleStateDTO.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.aries.jax.rs.rest.management.model;
+
+import org.osgi.dto.DTO;
+
+public class BundleStateDTO extends DTO {
+
+    public int state;
+
+    public int options;
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/BundlesDTO.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/BundlesDTO.java
new file mode 100644
index 0000000..25961e1
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/BundlesDTO.java
@@ -0,0 +1,33 @@
+/*
+ * 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.aries.jax.rs.rest.management.model;
+
+import java.util.List;
+
+import org.osgi.dto.DTO;
+
+public class BundlesDTO extends DTO {
+    public List<String> bundles;
+
+    public static BundlesDTO build(List<String> bundles) {
+        BundlesDTO bundlesDTO = new BundlesDTO();
+        bundlesDTO.bundles = bundles;
+        return bundlesDTO;
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/ServiceReferenceDTOs.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/ServiceReferenceDTOs.java
new file mode 100644
index 0000000..7f553e4
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/ServiceReferenceDTOs.java
@@ -0,0 +1,33 @@
+/*
+ * 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.aries.jax.rs.rest.management.model;
+
+import java.util.List;
+
+import org.osgi.framework.dto.ServiceReferenceDTO;
+
+public class ServiceReferenceDTOs {
+    public List<ServiceReferenceDTO> services;
+
+    public static ServiceReferenceDTOs build(List<ServiceReferenceDTO> services) {
+        ServiceReferenceDTOs serviceReferenceDTOs = new ServiceReferenceDTOs();
+        serviceReferenceDTOs.services = services;
+        return serviceReferenceDTOs;
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/ServicesDTO.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/ServicesDTO.java
new file mode 100644
index 0000000..1bf866e
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/ServicesDTO.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.aries.jax.rs.rest.management.model;
+
+import java.util.List;
+
+import org.osgi.dto.DTO;
+
+public class ServicesDTO extends DTO {
+
+    public List<String> services;
+
+    public static ServicesDTO build(List<String> services) {
+        ServicesDTO servicesDTO = new ServicesDTO();
+        servicesDTO.services = services;
+        return servicesDTO;
+    }
+
+}
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/package-info.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/package-info.java
new file mode 100644
index 0000000..4b23f73
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/model/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+@Export
+@Version("1.0.0")
+package org.apache.aries.jax.rs.rest.management.model;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/package-info.java b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/package-info.java
new file mode 100644
index 0000000..5621024
--- /dev/null
+++ b/integrations/rest-management/rest-management/src/main/java/org/apache/aries/jax/rs/rest/management/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+@Export
+@Version("1.0.0")
+package org.apache.aries.jax.rs.rest.management;
+
+import org.osgi.annotation.bundle.Export;
+import org.osgi.annotation.versioning.Version;
diff --git a/pom.xml b/pom.xml
index 9ba85d1..303f36e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -81,6 +81,17 @@
                 <scope>runtime</scope>
             </dependency>
             <dependency>
+                <groupId>com.sun.xml.bind</groupId>
+                <artifactId>jaxb-osgi</artifactId>
+                <version>${jaxb.version}</version>
+                <scope>runtime</scope>
+            </dependency>
+            <dependency>
+                <groupId>net.javacrumbs.json-unit</groupId>
+                <artifactId>json-unit-assertj</artifactId>
+                <version>2.24.0</version>
+            </dependency>
+            <dependency>
                 <groupId>org.apache.cxf</groupId>
                 <artifactId>cxf-tools-common</artifactId>
                 <version>${cxf.version}</version>
@@ -249,12 +260,32 @@
                 <version>1.5</version>
             </dependency>
             <dependency>
+                <groupId>org.apache.johnzon</groupId>
+                <artifactId>johnzon-core</artifactId>
+                <version>1.2.10</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.johnzon</groupId>
+                <artifactId>johnzon-mapper</artifactId>
+                <version>1.2.10</version>
+            </dependency>
+            <dependency>
+                <groupId>org.assertj</groupId>
+                <artifactId>assertj-core</artifactId>
+                <version>3.19.0</version>
+            </dependency>
+            <dependency>
                 <groupId>org.eclipse.platform</groupId>
                 <artifactId>org.eclipse.osgi</artifactId>
                 <version>3.16.100</version>
                 <scope>runtime</scope>
             </dependency>
             <dependency>
+                <groupId>org.hamcrest</groupId>
+                <artifactId>hamcrest-core</artifactId>
+                <version>2.2</version>
+            </dependency>
+            <dependency>
                 <groupId>org.osgi</groupId>
                 <artifactId>osgi.annotation</artifactId>
                 <version>8.0.0</version>
@@ -344,12 +375,11 @@
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
-
             <dependency>
-                <groupId>com.sun.xml.bind</groupId>
-                <artifactId>jaxb-osgi</artifactId>
-                <version>${jaxb.version}</version>
-                <scope>runtime</scope>
+                <groupId>org.xmlunit</groupId>
+                <artifactId>xmlunit-assertj3</artifactId>
+                <version>2.8.2</version>
+                <scope>compile</scope>
             </dependency>
         </dependencies>
     </dependencyManagement>
@@ -443,6 +473,10 @@
                                 <goal>resolve</goal>
                             </goals>
                             <phase>package</phase>
+                            <configuration>
+                                <reportOptional>false</reportOptional>
+                                <failOnChanges>false</failOnChanges>
+                            </configuration>
                         </execution>
                     </executions>
                 </plugin>