SLING-8419 move JSON configuration serialization code to dedicated
package with no dependencies to Sling Feature
diff --git a/src/main/java/org/apache/sling/feature/io/ConfiguratorUtil.java b/src/main/java/org/apache/sling/feature/io/ConfiguratorUtil.java
new file mode 100644
index 0000000..dfba0b5
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/io/ConfiguratorUtil.java
@@ -0,0 +1,175 @@
+/*
+ * 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.feature.io;
+
+import java.io.Writer;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+import javax.json.Json;
+import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+
+import org.apache.sling.feature.Configuration;
+
+/**
+ * Helper class to write JSON structures as defined in
+ * <a href="https://osgi.org/specification/osgi.cmpn/7.0.0/service.configurator.html#d0e131765">OSGi Configurator Specification 1.0</a>.
+ *
+ */
+public class ConfiguratorUtil {
+
+ private ConfiguratorUtil() {
+ }
+
+ protected static final JsonGenerator newGenerator(final Writer writer) {
+ JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true));
+
+ // prevent closing of the underlying writer
+ Writer closeShieldWriter = new CloseShieldWriter(writer);
+ return generatorFactory.createGenerator(closeShieldWriter);
+ }
+
+ /** Write the OSGi configuration to a JSON structure.
+ * The writer is not closed.
+ *
+ * @param writer Writer
+ * @param props The configuration properties to write */
+ public static void writeConfiguration(final Writer writer, final Dictionary<String, Object> props) {
+ try (JsonGenerator generator = newGenerator(writer)) {
+ generator.writeStartObject();
+ writeConfiguration(generator, props);
+ generator.writeEnd();
+ }
+ }
+
+ public static void writeConfiguration(final JsonGenerator generator, final Dictionary<String, Object> props) {
+ final Enumeration<String> e = props.keys();
+ while (e.hasMoreElements()) {
+ final String name = e.nextElement();
+ if (Configuration.PROP_ARTIFACT_ID.equals(name)) {
+ continue;
+ }
+ final Object val = props.get(name);
+ writeConfigurationProperty(generator, name, val);
+ }
+ }
+
+ private static void writeConfigurationProperty(JsonGenerator generator, String name, Object val) {
+ String dataType = getDataType(val);
+ writeConfigurationProperty(generator, name, dataType, val);
+ }
+
+ private static void writeConfigurationProperty(JsonGenerator generator, String name, String dataType, Object val) {
+ String nameWithDataPostFix = name;
+ if (dataType != null) {
+ nameWithDataPostFix += ":" + dataType;
+ }
+ if (val.getClass().isArray()) {
+ generator.writeStartArray(nameWithDataPostFix);
+ for (int i = 0; i < Array.getLength(val); i++) {
+ writeArrayItem(generator, Array.get(val, i));
+ }
+ generator.writeEnd();
+ } else if (val instanceof Collection) {
+ generator.writeStartArray(nameWithDataPostFix);
+ for (Object item : Collection.class.cast(val)) {
+ writeArrayItem(generator, item);
+ }
+ generator.writeEnd();
+ } else {
+ writeNameValuePair(generator, nameWithDataPostFix, val);
+ }
+ }
+
+ private static void writeNameValuePair(JsonGenerator generator, String name, Object item) {
+ if (item instanceof Boolean) {
+ generator.write(name, (Boolean) item);
+ } else if (item instanceof Long || item instanceof Integer || item instanceof Byte || item instanceof Short) {
+ generator.write(name, ((Number)item).longValue());
+ } else if (item instanceof Double) {
+ generator.write(name, (Double) item);
+ } else if (item instanceof Float) {
+ generator.write(name, (Float) item);
+ } else {
+ generator.write(name, item.toString());
+ }
+ }
+
+ private static void writeArrayItem(JsonGenerator generator, Object item) {
+ if (item instanceof Boolean) {
+ generator.write((Boolean) item);
+ } else if (item instanceof Long || item instanceof Integer || item instanceof Byte || item instanceof Short) {
+ generator.write(((Number)item).longValue());
+ } else if (item instanceof Double) {
+ generator.write((Double) item);
+ } else if (item instanceof Float) {
+ generator.write((Float) item);
+ } else {
+ generator.write(item.toString());
+ }
+ }
+
+ private static String getDataType(Object object) {
+ if (object instanceof Collection) {
+ // check class of first item
+ Iterator<?> it = ((Collection<?>) object).iterator();
+ if (it.hasNext()) {
+ Class<?> itemClass = it.next().getClass();
+ return "Collection<" + getDataType(itemClass, false) + ">";
+ } else {
+ throw new IllegalStateException("Empty collections are invalid");
+ }
+ } else {
+ return getDataType(object.getClass(), true);
+ }
+ }
+
+ private static String getDataType(Class<?> clazz, boolean allowEmpty) {
+ if (clazz.isArray()) {
+ String dataType = getDataType(clazz.getComponentType(), false);
+ if (dataType != null) {
+ return dataType + "[]";
+ } else {
+ return null;
+ }
+ }
+ // default classes used by native JSON types
+ else if (clazz.isAssignableFrom(Boolean.class) || clazz.isAssignableFrom(boolean.class) || clazz.isAssignableFrom(Long.class) || clazz.isAssignableFrom(long.class) ||
+ clazz.isAssignableFrom(Double.class) || clazz.isAssignableFrom(double.class) || clazz.isAssignableFrom(String.class)) {
+ // no data type necessary except when being used in an array/collection
+ if (!allowEmpty) {
+ // for all other cases just use the simple name
+ return clazz.getSimpleName();
+ }
+
+ } else if (clazz.isAssignableFrom(Integer.class) || clazz.isAssignableFrom(int.class) || clazz.isAssignableFrom(Float.class) || clazz.isAssignableFrom(float.class)
+ || clazz.isAssignableFrom(Byte.class) || clazz.isAssignableFrom(byte.class) || clazz.isAssignableFrom(Short.class) || clazz.isAssignableFrom(short.class)
+ || clazz.isAssignableFrom(Character.class) || clazz.isAssignableFrom(char.class)) {
+ return clazz.getSimpleName();
+ }
+ if (!allowEmpty) {
+ throw new IllegalStateException("Class does not have a valid type " + clazz);
+ }
+ return null;
+
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java b/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java
index 2e57abe..fa3572d 100644
--- a/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java
+++ b/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONWriter.java
@@ -50,130 +50,5 @@
generator.close();
}
- /** Write the OSGi configuration to a JSON structure as defined in
- * <a href="https://osgi.org/specification/osgi.cmpn/7.0.0/service.configurator.html#d0e131765">OSGi Configurator Specification 1.0</a>.
- * The writer is not closed.
- *
- * @param writer Writer
- * @param props The configuration properties to write */
- public static void writeConfiguration(final Writer writer, final Dictionary<String, Object> props) {
- final ConfigurationJSONWriter w = new ConfigurationJSONWriter();
- JsonGenerator generator = w.newGenerator(writer);
- generator.writeStartObject();
- writeConfiguration(generator, props);
- generator.writeEnd();
- generator.close();
- }
-
- protected static void writeConfiguration(final JsonGenerator generator, final Dictionary<String, Object> props) {
- final Enumeration<String> e = props.keys();
- while (e.hasMoreElements()) {
- final String name = e.nextElement();
- if (Configuration.PROP_ARTIFACT_ID.equals(name)) {
- continue;
- }
- final Object val = props.get(name);
- writeConfigurationProperty(generator, name, val);
- }
- }
-
- private static void writeConfigurationProperty(JsonGenerator generator, String name, Object val) {
- String dataType = getDataType(val);
- writeConfigurationProperty(generator, name, dataType, val);
- }
-
- private static void writeConfigurationProperty(JsonGenerator generator, String name, String dataType, Object val) {
- String nameWithDataPostFix = name;
- if (dataType != null) {
- nameWithDataPostFix += ":" + dataType;
- }
- if (val.getClass().isArray()) {
- generator.writeStartArray(nameWithDataPostFix);
- for (int i = 0; i < Array.getLength(val); i++) {
- writeArrayItem(generator, Array.get(val, i));
- }
- generator.writeEnd();
- } else if (val instanceof Collection) {
- generator.writeStartArray(nameWithDataPostFix);
- for (Object item : Collection.class.cast(val)) {
- writeArrayItem(generator, item);
- }
- generator.writeEnd();
- } else {
- writeNameValuePair(generator, nameWithDataPostFix, val);
- }
- }
-
- private static void writeNameValuePair(JsonGenerator generator, String name, Object item) {
- if (item instanceof Boolean) {
- generator.write(name, (Boolean) item);
- } else if (item instanceof Long || item instanceof Integer || item instanceof Byte || item instanceof Short) {
- generator.write(name, ((Number)item).longValue());
- } else if (item instanceof Double) {
- generator.write(name, (Double) item);
- } else if (item instanceof Float) {
- generator.write(name, (Float) item);
- } else {
- generator.write(name, item.toString());
- }
- }
-
- private static void writeArrayItem(JsonGenerator generator, Object item) {
- if (item instanceof Boolean) {
- generator.write((Boolean) item);
- } else if (item instanceof Long || item instanceof Integer || item instanceof Byte || item instanceof Short) {
- generator.write(((Number)item).longValue());
- } else if (item instanceof Double) {
- generator.write((Double) item);
- } else if (item instanceof Float) {
- generator.write((Float) item);
- } else {
- generator.write(item.toString());
- }
- }
-
- private static String getDataType(Object object) {
- if (object instanceof Collection) {
- // check class of first item
- Iterator<?> it = ((Collection<?>) object).iterator();
- if (it.hasNext()) {
- Class<?> itemClass = it.next().getClass();
- return "Collection<" + getDataType(itemClass, false) + ">";
- } else {
- throw new IllegalStateException("Empty collections are invalid");
- }
- } else {
- return getDataType(object.getClass(), true);
- }
- }
-
- private static String getDataType(Class<?> clazz, boolean allowEmpty) {
- if (clazz.isArray()) {
- String dataType = getDataType(clazz.getComponentType(), false);
- if (dataType != null) {
- return dataType + "[]";
- } else {
- return null;
- }
- }
- // default classes used by native JSON types
- else if (clazz.isAssignableFrom(Boolean.class) || clazz.isAssignableFrom(boolean.class) || clazz.isAssignableFrom(Long.class) || clazz.isAssignableFrom(long.class) ||
- clazz.isAssignableFrom(Double.class) || clazz.isAssignableFrom(double.class) || clazz.isAssignableFrom(String.class)) {
- // no data type necessary except when being used in an array/collection
- if (!allowEmpty) {
- // for all other cases just use the simple name
- return clazz.getSimpleName();
- }
-
- } else if (clazz.isAssignableFrom(Integer.class) || clazz.isAssignableFrom(int.class) || clazz.isAssignableFrom(Float.class) || clazz.isAssignableFrom(float.class)
- || clazz.isAssignableFrom(Byte.class) || clazz.isAssignableFrom(byte.class) || clazz.isAssignableFrom(Short.class) || clazz.isAssignableFrom(short.class)
- || clazz.isAssignableFrom(Character.class) || clazz.isAssignableFrom(char.class)) {
- return clazz.getSimpleName();
- }
- if (!allowEmpty) {
- throw new IllegalStateException("Class does not have a valid type " + clazz);
- }
- return null;
-
- }
+
}
diff --git a/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java b/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
index d88f288..b74a4db 100644
--- a/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
+++ b/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
@@ -36,6 +36,7 @@
import org.apache.sling.feature.MatchingRequirement;
import org.apache.sling.feature.Prototype;
import org.apache.sling.feature.io.CloseShieldWriter;
+import org.apache.sling.feature.io.ConfiguratorUtil;
import org.osgi.resource.Capability;
import org.osgi.resource.Requirement;
@@ -105,7 +106,7 @@
for(final Configuration cfg : cfgs) {
generator.writeStartObject(cfg.getPid());
- ConfigurationJSONWriter.writeConfiguration(generator, cfg.getConfigurationProperties());
+ ConfiguratorUtil.writeConfiguration(generator, cfg.getConfigurationProperties());
generator.writeEnd();
}
diff --git a/src/test/java/org/apache/sling/feature/io/json/ConfigurationJSONWriterTest.java b/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java
similarity index 96%
rename from src/test/java/org/apache/sling/feature/io/json/ConfigurationJSONWriterTest.java
rename to src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java
index 8dac7c1..46b6ba8 100644
--- a/src/test/java/org/apache/sling/feature/io/json/ConfigurationJSONWriterTest.java
+++ b/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sling.feature.io.json;
+package org.apache.sling.feature.io;
import java.io.IOException;
import java.io.StringWriter;
@@ -30,6 +30,7 @@
import org.apache.felix.configurator.impl.json.JSONUtil;
import org.apache.felix.configurator.impl.json.TypeConverter;
import org.apache.felix.configurator.impl.model.ConfigurationFile;
+import org.apache.sling.feature.io.ConfiguratorUtil;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.hamcrest.core.Every;
@@ -39,7 +40,7 @@
import org.osgi.util.converter.Converters;
import org.osgi.util.converter.TypeReference;
-public class ConfigurationJSONWriterTest {
+public class ConfiguratorUtilTest {
@Test
public void testConfigurationWriteReadRoundtrip() throws IOException {
@@ -80,7 +81,7 @@
props.put("String-array", new String[]{"test1", "test2"});
props.put("String-list", Arrays.asList("test1", "test2"));
StringWriter writer = new StringWriter();
- ConfigurationJSONWriter.writeConfiguration(writer, props);
+ ConfiguratorUtil.writeConfiguration(writer, props);
writer.close();
assertConfigurationJson(writer.toString(), props);
}