You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2018/06/21 12:33:22 UTC
[sling-org-apache-sling-capabilities] 01/01: Initial commit,
moving from sling-whiteboard
This is an automated email from the ASF dual-hosted git repository.
bdelacretaz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-capabilities.git
commit e50900fb113e36303023aae73e4fb97f13689823
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Thu Jun 21 14:25:30 2018 +0200
Initial commit, moving from sling-whiteboard
---
.gitignore | 8 ++
README.md | 48 ++++++++
pom.xml | 124 +++++++++++++++++++++
.../sling/capabilities/CapabilitiesSource.java | 43 +++++++
.../capabilities/internal/CapabilitiesServlet.java | 61 ++++++++++
.../internal/JSONCapabilitiesWriter.java | 61 ++++++++++
.../internal/CapabilitesServletTest.java | 78 +++++++++++++
.../internal/JSONCapabilitiesWriterTest.java | 76 +++++++++++++
.../sling/capabilities/internal/MockSource.java | 51 +++++++++
9 files changed, 550 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b018844
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.project
+.classpath
+.settings
+target
+bin
+*.iml
+.idea
+.DS_Store
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b84cb65
--- /dev/null
+++ b/README.md
@@ -0,0 +1,48 @@
+Sling Capabilities Module
+=========================
+
+The servlet provided by this module allows for creating Capabilities HTTP endpoints
+on a Sling instance: Resources that provide information on which services are available,
+version levels etc.
+
+For now, a single type of endpoint is provided: all Resources which have the
+`sling/capabilities` resource type will return the same set of Capabilities, generated
+by aggregating the output of all active `CapabilitiesSource` services.
+
+This can be easily expanded to multiple sets of Capabilities if needed later on,
+by changing the code to use service properties to group or tag the `CapabilitiesSource` services.
+
+The tests provide simple `CapabilitiesSource` examples, that API is as follows:
+
+ @ProviderType
+ public interface CapabilitiesSource {
+
+ /** @return the namespace to use to group our capabilities.
+ * That name must be unique in a given Sling instance.
+ */
+ String getNamespace();
+
+ /** @return zero to N capabilities, each being represented by
+ * a key/value pair.
+ * @throws Exception if the capabilities could not be computed.
+ */
+ Map<String, Object> getCapabilities() throws Exception;
+ }
+
+The `CapabilitiesServlet` produces output as in the example below, where two
+`CapabilitiesSource` services are available:
+
+ $ curl -s -u admin:admin http://localhost:8080/tmp/cap.json | jq .
+ {
+ "org.apache.sling.capabilities": {
+ "org.apache.sling.capabilities.internal.OsgiFrameworkCapabilitiesSource": {
+ "framework.bundle.symbolic.name": "org.apache.felix.framework",
+ "framework.bundle.version": "5.6.10"
+ },
+ "org.apache.sling.capabilities.internal.JvmCapabilitiesSource": {
+ "java.specification.version": "1.8",
+ "java.vm.version": "25.171-b11",
+ "java.vm.vendor": "Oracle Corporation"
+ }
+ }
+ }
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c566b9e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,124 @@
+<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>sling</artifactId>
+ <groupId>org.apache.sling</groupId>
+ <version>30</version>
+ <relativePath/>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <name>Apache Sling Capabilities</name>
+ <artifactId>org.apache.sling.capabilities</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+ <properties>
+ <!-- This module can work with older versions of Sling
+ as well, so be conservative here -->
+ <sling.java.version>7</sling.java.version>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Embed-Dependency>
+ org.apache.felix.utils;inline=org/apache/felix/utils/json/JSONWriter.class
+ </Embed-Dependency>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>biz.aQute.bndlib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.api</artifactId>
+ <version>2.16.4</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.utils</artifactId>
+ <version>1.9.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.cmpn</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <!-- TESTING -->
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.johnzon</artifactId>
+ <version>1.0.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.osgi-mock</artifactId>
+ <version>2.3.8</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.testing.sling-mock</artifactId>
+ <version>2.2.18</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/capabilities/CapabilitiesSource.java b/src/main/java/org/apache/sling/capabilities/CapabilitiesSource.java
new file mode 100644
index 0000000..e6ec9d5
--- /dev/null
+++ b/src/main/java/org/apache/sling/capabilities/CapabilitiesSource.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.sling.capabilities;
+
+import java.util.Map;
+import org.osgi.annotation.versioning.ProviderType;
+
+/** A CapabilitiesSource provides capabilities, as a Map of key/value
+ * pairs.
+ * Various types of CapabilitiesSources are meant
+ * to be implemented, using Health Checks, OSGi environment status
+ * or any suitable input to find out which capabilities are present.
+ */
+@ProviderType
+public interface CapabilitiesSource {
+
+ /** @return the namespace to use to group our capabilities.
+ * That name must be unique in a given Sling instance.
+ */
+ String getNamespace();
+
+ /** @return zero to N capabilities, each being represented by
+ * a key/value pair.
+ * @throws Exception if the capabilities could not be computed.
+ */
+ Map<String, Object> getCapabilities() throws Exception;
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/capabilities/internal/CapabilitiesServlet.java b/src/main/java/org/apache/sling/capabilities/internal/CapabilitiesServlet.java
new file mode 100644
index 0000000..35e2819
--- /dev/null
+++ b/src/main/java/org/apache/sling/capabilities/internal/CapabilitiesServlet.java
@@ -0,0 +1,61 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.capabilities.internal;
+
+import java.io.IOException;
+import java.util.List;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
+import org.apache.sling.capabilities.CapabilitiesSource;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+
+@Component(service = Servlet.class,
+property = {
+ "sling.servlet.resourceTypes=sling/capabilities",
+ "sling.servlet.methods=GET",
+ "sling.servlet.selectors=capabilities",
+ "sling.servlet.extensions=json"
+})
+
+public class CapabilitiesServlet extends SlingSafeMethodsServlet {
+
+ @Reference(
+ policy=ReferencePolicy.DYNAMIC,
+ cardinality=ReferenceCardinality.AT_LEAST_ONE,
+ policyOption=ReferencePolicyOption.GREEDY)
+ volatile List<CapabilitiesSource> sources;
+
+ CapabilitiesServlet() {
+ }
+
+ @Override
+ protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
+ response.setContentType("application/json");
+ response.setCharacterEncoding("UTF-8");
+ new JSONCapabilitiesWriter().writeJson(response.getWriter(), sources);
+ response.getWriter().flush();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/capabilities/internal/JSONCapabilitiesWriter.java b/src/main/java/org/apache/sling/capabilities/internal/JSONCapabilitiesWriter.java
new file mode 100644
index 0000000..3552941
--- /dev/null
+++ b/src/main/java/org/apache/sling/capabilities/internal/JSONCapabilitiesWriter.java
@@ -0,0 +1,61 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.capabilities.internal;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.felix.utils.json.JSONWriter;
+import org.apache.sling.capabilities.CapabilitiesSource;
+
+/** Create the JSON output of our servlet */
+class JSONCapabilitiesWriter {
+
+ static final String CAPS_KEY = "org.apache.sling.capabilities";
+
+ /** Write JSON to the supplied Writer, using the supplied sources */
+ void writeJson(Writer w, Collection<CapabilitiesSource> sources) throws IOException {
+ final JSONWriter jw = new JSONWriter(w);
+ jw.object();
+ jw.key(CAPS_KEY);
+ jw.object();
+
+ Map<String, Object> values = null;
+ for(CapabilitiesSource s : sources) {
+ try {
+ values = s.getCapabilities();
+ } catch(Exception e) {
+ values = new HashMap<>();
+ values.put("_EXCEPTION_", e.getClass().getName() + ":" + e.getMessage());
+ }
+ jw.key(s.getNamespace());
+ jw.object();
+ for(Map.Entry<String, Object> e : values.entrySet()) {
+ jw.key(e.getKey());
+ jw.value(e.getValue());
+ }
+ jw.endObject();
+ }
+
+ jw.endObject();
+ jw.endObject();
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/capabilities/internal/CapabilitesServletTest.java b/src/test/java/org/apache/sling/capabilities/internal/CapabilitesServletTest.java
new file mode 100644
index 0000000..80017e0
--- /dev/null
+++ b/src/test/java/org/apache/sling/capabilities/internal/CapabilitesServletTest.java
@@ -0,0 +1,78 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.capabilities.internal;
+
+import java.io.IOException;
+import java.io.StringReader;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import javax.servlet.ServletException;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
+import org.apache.sling.capabilities.CapabilitiesSource;
+import org.apache.sling.servlethelpers.MockSlingHttpServletRequest;
+import org.apache.sling.servlethelpers.MockSlingHttpServletResponse;
+import org.apache.sling.testing.mock.osgi.MockOsgi;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.apache.sling.testing.mock.sling.MockSling;
+import static org.junit.Assert.assertEquals;
+import org.osgi.framework.BundleContext;
+
+/** Test the JSONCapabilitiesWriter */
+public class CapabilitesServletTest {
+
+ private SlingSafeMethodsServlet servlet;
+
+ @Rule
+ public final OsgiContext context = new OsgiContext();
+
+ private BundleContext bundleContext;
+
+ @Before
+ public void setup() {
+ servlet = new CapabilitiesServlet();
+ bundleContext = MockOsgi.newBundleContext();
+
+ bundleContext.registerService(CapabilitiesSource.class.getName(), new MockSource("F", 2), null);
+ bundleContext.registerService(CapabilitiesSource.class.getName(), new MockSource("G", 43), null);
+
+ MockOsgi.injectServices(servlet, bundleContext);
+ }
+
+ @Test
+ public void testServlet() throws ServletException, IOException {
+ final ResourceResolver resolver = MockSling.newResourceResolver(bundleContext);
+ MockSlingHttpServletRequest req = new MockSlingHttpServletRequest(resolver);
+ MockSlingHttpServletResponse resp = new MockSlingHttpServletResponse();
+
+ servlet.service(req, resp);
+
+ // Just verify that both sources are taken into account
+ // the JSON format details are tested elsewhere
+ final JsonReader r = Json.createReader(new StringReader(resp.getOutputAsString()));
+ final JsonObject rootJson = r.readObject();
+ final JsonObject json = rootJson.getJsonObject(JSONCapabilitiesWriter.CAPS_KEY);
+ assertEquals("VALUE_1_F", json.getJsonObject("F").getString("KEY_1_F"));
+ assertEquals("VALUE_42_G", json.getJsonObject("G").getString("KEY_42_G"));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/capabilities/internal/JSONCapabilitiesWriterTest.java b/src/test/java/org/apache/sling/capabilities/internal/JSONCapabilitiesWriterTest.java
new file mode 100644
index 0000000..dd7227a
--- /dev/null
+++ b/src/test/java/org/apache/sling/capabilities/internal/JSONCapabilitiesWriterTest.java
@@ -0,0 +1,76 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.capabilities.internal;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonReader;
+import org.apache.sling.capabilities.CapabilitiesSource;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/** Test the JSONCapabilitiesWriter */
+public class JSONCapabilitiesWriterTest {
+
+ @Test
+ public void testWithTwoSources() throws IOException {
+ final List<CapabilitiesSource> sources = new ArrayList<>();
+ sources.add(new MockSource("A", 2));
+ sources.add(new MockSource("B", 1));
+
+ final StringWriter w = new StringWriter();
+ new JSONCapabilitiesWriter().writeJson(w, sources);
+
+ final JsonReader r = Json.createReader(new StringReader(w.toString()));
+ final JsonObject rootJson = r.readObject();
+ final JsonObject json = rootJson.getJsonObject(JSONCapabilitiesWriter.CAPS_KEY);
+ assertEquals("VALUE_0_A", json.getJsonObject("A").getString("KEY_0_A"));
+ assertEquals("VALUE_1_A", json.getJsonObject("A").getString("KEY_1_A"));
+ assertEquals("VALUE_0_B", json.getJsonObject("B").getString("KEY_0_B"));
+
+ assertEquals("Expecting 1 root key", 1, rootJson.keySet().size());
+ assertEquals("Expecting 2 keys at A", 2, json.getJsonObject("A").keySet().size());
+ assertEquals("Expecting 1 key at B", 1, json.getJsonObject("B").keySet().size());
+ }
+
+ @Test
+ public void testWithException() throws IOException {
+ final List<CapabilitiesSource> sources = new ArrayList<>();
+ sources.add(new MockSource("A", 1));
+ sources.add(new MockSource("EXCEPTION", 2));
+ sources.add(new MockSource("B", 1));
+
+ final StringWriter w = new StringWriter();
+ new JSONCapabilitiesWriter().writeJson(w, sources);
+
+ final JsonReader r = Json.createReader(new StringReader(w.toString()));
+ final JsonObject rootJson = r.readObject();
+ final JsonObject json = rootJson.getJsonObject(JSONCapabilitiesWriter.CAPS_KEY);
+ assertEquals("VALUE_0_A", json.getJsonObject("A").getString("KEY_0_A"));
+ assertEquals("java.lang.IllegalArgumentException:Simulating a problem", json.getJsonObject("EXCEPTION").getString("_EXCEPTION_"));
+ assertEquals("VALUE_0_B", json.getJsonObject("B").getString("KEY_0_B"));
+
+ assertEquals("Expecting 1 key at EXCEPTION", 1, json.getJsonObject("EXCEPTION").keySet().size());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/capabilities/internal/MockSource.java b/src/test/java/org/apache/sling/capabilities/internal/MockSource.java
new file mode 100644
index 0000000..50b8652
--- /dev/null
+++ b/src/test/java/org/apache/sling/capabilities/internal/MockSource.java
@@ -0,0 +1,51 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+package org.apache.sling.capabilities.internal;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.sling.capabilities.CapabilitiesSource;
+
+class MockSource implements CapabilitiesSource {
+
+ private final String namespace;
+ private final Map<String, Object> props = new HashMap<>();
+
+ MockSource(String namespace, int propsCount) {
+ this.namespace = namespace;
+ for (int i = 0; i < propsCount; i++) {
+ props.put("KEY_" + i + "_" + namespace, "VALUE_" + i + "_" + namespace);
+ }
+ }
+
+ @Override
+ public Map<String, Object> getCapabilities() throws Exception {
+ if (namespace.contains("EXCEPTION")) {
+ throw new IllegalArgumentException("Simulating a problem");
+ }
+ return Collections.unmodifiableMap(props);
+ }
+
+ @Override
+ public String getNamespace() {
+ return namespace;
+ }
+
+}