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>