You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2020/04/19 17:06:14 UTC
[sling-org-apache-sling-feature-io] branch master updated:
SLING-9362 : Use Apache Felix cm.json for JSON handling
This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-io.git
The following commit(s) were added to refs/heads/master by this push:
new 974e001 SLING-9362 : Use Apache Felix cm.json for JSON handling
974e001 is described below
commit 974e0018b41c564ca2fb418ccaae51306878819a
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sun Apr 19 19:06:04 2020 +0200
SLING-9362 : Use Apache Felix cm.json for JSON handling
---
bnd.bnd | 4 +-
pom.xml | 27 +-
.../feature/io/json/ConfigurationJSONReader.java | 46 +-
.../feature/io/json/ConfigurationJSONWriter.java | 35 +-
.../sling/feature/io/json/FeatureJSONReader.java | 645 +++++++++++++++++--
.../sling/feature/io/json/FeatureJSONWriter.java | 305 ++++++++-
.../sling/feature/io/json/JSONReaderBase.java | 696 ---------------------
.../sling/feature/io/json/JSONWriterBase.java | 311 ---------
.../sling/feature/io/json/ManifestUtils.java | 23 +-
.../sling/feature/io/ConfiguratorUtilTest.java | 27 +-
.../org/apache/sling/feature/io/IOUtilsTest.java | 158 ++---
.../feature/io/artifacts/ArtifactManagerTest.java | 3 -
12 files changed, 1076 insertions(+), 1204 deletions(-)
diff --git a/bnd.bnd b/bnd.bnd
index b514d8b..62f82d8 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -1,5 +1,3 @@
--conditionalpackage: org.apache.felix.configurator.impl.json,\\
- org.apache.felix.configurator.impl.model,\\
- org.apache.felix.utils.collections,\\
+-conditionalpackage: org.apache.felix.utils.collections,\\
org.apache.felix.utils.resource,\\
org.apache.felix.utils.version
diff --git a/pom.xml b/pom.xml
index 65ad314..3c28398 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,13 @@
<build>
<plugins>
<plugin>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>bnd-baseline-maven-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
@@ -105,31 +112,31 @@
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.converter</artifactId>
- <version>1.0.8</version>
+ <version>1.0.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.configurator</artifactId>
- <version>1.0.10</version>
+ <artifactId>org.apache.felix.cm.json</artifactId>
+ <version>1.0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.utils</artifactId>
- <version>1.11.2</version>
+ <version>1.11.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
- <artifactId>geronimo-json_1.0_spec</artifactId>
- <version>1.0-alpha-1</version>
+ <artifactId>geronimo-json_1.1_spec</artifactId>
+ <version>1.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.feature</artifactId>
- <version>1.1.6</version>
+ <version>1.1.8</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -168,6 +175,12 @@
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-core</artifactId>
+ <version>1.2.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.util.function</artifactId>
<version>1.0.0</version>
<scope>test</scope>
</dependency>
diff --git a/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java b/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java
index d330482..7d4c555 100644
--- a/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java
+++ b/src/main/java/org/apache/sling/feature/io/json/ConfigurationJSONReader.java
@@ -18,20 +18,18 @@ package org.apache.sling.feature.io.json;
import java.io.IOException;
import java.io.Reader;
-import java.io.StringReader;
-import java.util.Collections;
+import java.util.Hashtable;
import java.util.Map;
-import javax.json.Json;
-import javax.json.JsonObject;
-
-import org.apache.felix.configurator.impl.json.JSONUtil;
+import org.apache.felix.cm.json.ConfigurationReader;
+import org.apache.felix.cm.json.ConfigurationResource;
+import org.apache.sling.feature.Configuration;
import org.apache.sling.feature.Configurations;
/**
* JSON Reader for configurations.
*/
-public class ConfigurationJSONReader extends JSONReaderBase {
+public class ConfigurationJSONReader {
/**
* Read a map of configurations from the reader
@@ -45,32 +43,28 @@ public class ConfigurationJSONReader extends JSONReaderBase {
public static Configurations read(final Reader reader, final String location)
throws IOException {
try {
- final ConfigurationJSONReader mr = new ConfigurationJSONReader(location);
- return mr.readConfigurations(reader);
+ final ConfigurationJSONReader mr = new ConfigurationJSONReader();
+ return mr.readConfigurations(location, reader);
} catch (final IllegalStateException | IllegalArgumentException e) {
throw new IOException(e);
}
}
- /**
- * Private constructor
- * @param location Optional location
- */
- ConfigurationJSONReader(final String location) {
- super(location);
- }
-
- Configurations readConfigurations(final Reader reader) throws IOException {
+ Configurations readConfigurations(final String location, final Reader reader) throws IOException {
final Configurations result = new Configurations();
- final JsonObject json = Json.createReader(new StringReader(minify(reader))).readObject();
-
- @SuppressWarnings("unchecked")
- final Map<String, Object> map = (Map<String, Object>) JSONUtil.getValue(json);
-
- final Map<String, Object> objMap = Collections.singletonMap(JSONConstants.FEATURE_CONFIGURATIONS, (Object)map);
-
- readConfigurations(objMap, result);
+ final ConfigurationReader cfgReader = org.apache.felix.cm.json.Configurations
+ .buildReader()
+ .withIdentifier(location)
+ .verifyAsBundleResource(true)
+ .build(reader);
+ final ConfigurationResource rsrc = cfgReader.readConfigurationResource();
+ for(Map.Entry<String, Hashtable<String, Object>> entry : rsrc.getConfigurations().entrySet() ) {
+ final Configuration cf = new Configuration(entry.getKey());
+ for(final Map.Entry<String, Object> prop : entry.getValue().entrySet()) {
+ cf.getProperties().put(prop.getKey(), prop.getValue());
+ }
+ }
return result;
}
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 a6692a4..eb259ba 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
@@ -18,16 +18,19 @@ package org.apache.sling.feature.io.json;
import java.io.IOException;
import java.io.Writer;
+import java.util.Collections;
+import java.util.Hashtable;
-import javax.json.stream.JsonGenerator;
-
+import org.apache.felix.cm.json.ConfigurationResource;
+import org.apache.felix.cm.json.ConfigurationWriter;
+import org.apache.sling.feature.Configuration;
import org.apache.sling.feature.Configurations;
/** JSON writer for configurations */
-public class ConfigurationJSONWriter extends JSONWriterBase {
+public class ConfigurationJSONWriter {
/** Writes the configurations to the writer. The writer is not closed.
- *
+ *
* @param writer Writer
* @param configs List of configurations
* @throws IOException If writing fails */
@@ -39,10 +42,26 @@ public class ConfigurationJSONWriter extends JSONWriterBase {
private void writeConfigurations(final Writer writer, final Configurations configs)
throws IOException {
- JsonGenerator generator = newGenerator(writer);
- writeConfigurations(generator, configs);
- generator.close();
+
+ final ConfigurationWriter cfgWriter = org.apache.felix.cm.json.Configurations
+ .buildWriter()
+ .build(writer);
+
+ final ConfigurationResource rsrc = new ConfigurationResource();
+ for(final Configuration cfg : configs) {
+ final Hashtable<String, Object> properties;
+ if ( cfg.getProperties() instanceof Hashtable ) {
+ properties = (Hashtable<String, Object>)cfg.getProperties();
+ } else {
+ properties = org.apache.felix.cm.json.Configurations.newConfiguration();
+ for(final String name : Collections.list(cfg.getProperties().keys()) ) {
+ properties.put(name, cfg.getProperties().get(name));
+ }
+ }
+ rsrc.getConfigurations().put(cfg.getPid(), properties);
+ }
+ cfgWriter.writeConfigurationResource(rsrc);
}
-
+
}
diff --git a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
index 54757af..b18a9f4 100644
--- a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
+++ b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONReader.java
@@ -18,20 +18,48 @@ package org.apache.sling.feature.io.json;
import java.io.IOException;
import java.io.Reader;
-import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.function.BiConsumer;
import javax.json.Json;
+import javax.json.JsonArray;
import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonStructure;
+import javax.json.JsonValue;
+import javax.json.JsonValue.ValueType;
import javax.json.stream.JsonParsingException;
+import org.apache.felix.cm.json.ConfigurationReader;
+import org.apache.felix.cm.json.ConfigurationResource;
+import org.apache.felix.utils.resource.CapabilityImpl;
+import org.apache.felix.utils.resource.RequirementImpl;
+import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Bundles;
+import org.apache.sling.feature.Configuration;
+import org.apache.sling.feature.Configurations;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
+import org.apache.sling.feature.ExtensionType;
+import org.apache.sling.feature.Extensions;
import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.MatchingRequirement;
+import org.apache.sling.feature.Prototype;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
/**
* This class offers a method to read a {@code Feature} using a {@code Reader} instance.
*/
-public class FeatureJSONReader extends JSONReaderBase {
+public class FeatureJSONReader {
/**
* Read a new feature from the reader
@@ -53,14 +81,557 @@ public class FeatureJSONReader extends JSONReaderBase {
}
/** The read feature. */
- protected Feature feature;
+ private Feature feature;
+
+ /** The optional location. */
+ private final String location;
+
+ /** Exception prefix containing the location (if set) */
+ private final String exceptionPrefix;
/**
- * Private constructor
+ * Protected constructor
* @param location Optional location
*/
protected FeatureJSONReader(final String location) {
- super(location);
+ this.location = location;
+ if ( location == null ) {
+ exceptionPrefix = "";
+ } else {
+ exceptionPrefix = location.concat(" : ");
+ }
+ }
+
+ protected ArtifactId getFeatureId(final JsonObject json) throws IOException {
+ if ( !json.containsKey(JSONConstants.FEATURE_ID) ) {
+ throw new IOException(this.exceptionPrefix.concat("Feature id is missing"));
+ }
+ final JsonValue idObj = json.get(JSONConstants.FEATURE_ID);
+ return ArtifactId.parse(checkTypeString(JSONConstants.FEATURE_ID, idObj));
+ }
+
+ private String getProperty(final JsonObject json, final String key) throws IOException {
+ final JsonValue val = json.get(key);
+ if ( val != null ) {
+ return checkTypeString(key, val);
+ }
+ return null;
+ }
+
+ /**
+ * Read the variables section
+ * @param json The json describing the feature or application
+ * @param kvMap The variables will be written to this Key Value Map
+ * @return The same variables as a normal map
+ * @throws IOException If the json is invalid.
+ */
+ private Map<String, String> readVariables(JsonObject json, Map<String,String> kvMap) throws IOException {
+ HashMap<String, String> variables = new HashMap<>();
+
+ if (json.containsKey(JSONConstants.FEATURE_VARIABLES)) {
+ final JsonValue variablesObj = json.get(JSONConstants.FEATURE_VARIABLES);
+
+ for (final Map.Entry<String, JsonValue> entry : checkTypeObject(JSONConstants.FEATURE_VARIABLES, variablesObj).entrySet()) {
+ // skip comments
+ if ( !entry.getKey().startsWith("#") ) {
+ JsonValue val = entry.getValue();
+ checkType("variable value", val, ValueType.STRING, ValueType.NUMBER, ValueType.FALSE, ValueType.TRUE, ValueType.NULL, null);
+
+ String key = entry.getKey();
+ if (kvMap.get(key) != null) {
+ throw new IOException(this.exceptionPrefix.concat("Duplicate variable ").concat(key));
+ }
+
+ Object convertedVal = org.apache.felix.cm.json.Configurations.convertToObject(val);
+ String value = convertedVal == null ? null : convertedVal.toString();
+
+ kvMap.put(key, value);
+ variables.put(key, value);
+ }
+ }
+ }
+ return variables;
+ }
+
+
+ /**
+ * Read the bundles / start levels section
+ * @param json The json object describing the feature
+ * @param container The bundles container
+ * @param configContainer The configurations container
+ * @throws IOException If the json is invalid.
+ */
+ private void readBundles(
+ final JsonObject json,
+ final Bundles container,
+ final Configurations configContainer) throws IOException {
+ if ( json.containsKey(JSONConstants.FEATURE_BUNDLES)) {
+ final JsonValue bundlesObj = json.get(JSONConstants.FEATURE_BUNDLES);
+ checkType(JSONConstants.FEATURE_BUNDLES, bundlesObj, ValueType.ARRAY);
+
+ final List<Artifact> list = new ArrayList<>();
+ readArtifacts(JSONConstants.FEATURE_BUNDLES, "bundle", list, bundlesObj, configContainer);
+
+ for(final Artifact a : list) {
+ if ( container.containsExact(a.getId())) {
+ throw new IOException(exceptionPrefix + "Duplicate identical bundle " + a.getId().toMvnId());
+ }
+ try {
+ // check start order
+ a.getStartOrder();
+ } catch ( final IllegalArgumentException nfe) {
+ throw new IOException(exceptionPrefix + "Illegal start order '" + a.getMetadata().get(Artifact.KEY_START_ORDER) + "'");
+ }
+ container.add(a);
+ }
+ }
+ }
+
+ private void readArtifacts(final String section,
+ final String artifactType,
+ final List<Artifact> artifacts,
+ final JsonValue listObj,
+ final Configurations container)
+ throws IOException {
+ checkType(section, listObj, ValueType.ARRAY);
+ for(final JsonValue entry : (JsonArray)listObj) {
+ final Artifact artifact;
+ checkType(artifactType, entry, ValueType.OBJECT, ValueType.STRING);
+ if ( entry.getValueType() == ValueType.STRING ) {
+ final String value = org.apache.felix.cm.json.Configurations.convertToObject(entry).toString();
+ // skip comments
+ if ( value.startsWith("#") ) {
+ continue;
+ }
+ artifact = new Artifact(ArtifactId.parse(value));
+ } else {
+ final JsonObject bundleObj = (JsonObject) entry;
+ if ( !bundleObj.containsKey(JSONConstants.ARTIFACT_ID) ) {
+ throw new IOException(exceptionPrefix.concat(" ").concat(artifactType).concat(" is missing required artifact id"));
+ }
+ final String value = checkTypeString(artifactType.concat(" ").concat(JSONConstants.ARTIFACT_ID), bundleObj.get(JSONConstants.ARTIFACT_ID));
+ final ArtifactId id = ArtifactId.parse(value);
+
+ artifact = new Artifact(id);
+ for(final Map.Entry<String, JsonValue> metadataEntry : bundleObj.entrySet()) {
+ final String key = metadataEntry.getKey();
+ // skip comments
+ if ( key.startsWith("#") ) {
+ continue;
+ }
+ if ( JSONConstants.ARTIFACT_KNOWN_PROPERTIES.contains(key) ) {
+ continue;
+ }
+ checkType(artifactType.concat(" metadata ").concat(key), metadataEntry.getValue(), ValueType.STRING, ValueType.FALSE, ValueType.TRUE, ValueType.NUMBER);
+ final String mval = org.apache.felix.cm.json.Configurations.convertToObject( metadataEntry.getValue()).toString();
+ artifact.getMetadata().put(key, mval);
+ }
+ if ( bundleObj.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
+ final JsonObject cfgs = checkTypeObject(artifactType.concat(" configurations"), bundleObj.get(JSONConstants.FEATURE_CONFIGURATIONS));
+ addConfigurations(cfgs, artifact, container);
+ }
+ }
+ artifacts.add(artifact);
+ }
+ }
+
+ private void addConfigurations(final JsonObject json,
+ final Artifact artifact,
+ final Configurations container) throws IOException {
+ final ConfigurationReader reader = org.apache.felix.cm.json.Configurations.buildReader()
+ .verifyAsBundleResource(true)
+ .withIdentifier(this.location)
+ .build(json);
+ final ConfigurationResource rsrc = reader.readConfigurationResource();
+ if ( !reader.getIgnoredErrors().isEmpty() ) {
+ final StringBuilder builder = new StringBuilder(exceptionPrefix);
+ builder.append("Errors in configurations:");
+ for(final String w : reader.getIgnoredErrors()) {
+ builder.append("\n");
+ builder.append(w);
+ }
+ throw new IOException(builder.toString());
+ }
+
+ for(final Map.Entry<String, Hashtable<String, Object>> c : rsrc.getConfigurations().entrySet()) {
+ final Configuration config = new Configuration(c.getKey());
+
+ for(final Map.Entry<String, Object> prop : c.getValue().entrySet()) {
+ config.getProperties().put(prop.getKey(), prop.getValue());
+ }
+ if ( config.getProperties().get(Configuration.PROP_ARTIFACT_ID) != null ) {
+ throw new IOException(exceptionPrefix.concat("Configuration must not define property ").concat(Configuration.PROP_ARTIFACT_ID));
+ }
+ if ( artifact != null ) {
+ config.getProperties().put(Configuration.PROP_ARTIFACT_ID, artifact.getId().toMvnId());
+ }
+ for(final Configuration current : container) {
+ if ( current.equals(config) ) {
+ throw new IOException(exceptionPrefix.concat("Duplicate configuration ").concat(config.getPid()));
+ }
+ }
+ container.add(config);
+ }
+ }
+
+
+ private void readConfigurations(final JsonObject json,
+ final Configurations container) throws IOException {
+ if ( json.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
+ final JsonObject cfgs = checkTypeObject(JSONConstants.FEATURE_CONFIGURATIONS, json.get(JSONConstants.FEATURE_CONFIGURATIONS));
+ addConfigurations(cfgs, null, container);
+ }
+ }
+
+ private void readFrameworkProperties(final JsonObject json,
+ final Map<String,String> container) throws IOException {
+ if ( json.containsKey(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES) ) {
+ final JsonValue propsObj= json.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
+
+ for(final Map.Entry<String, JsonValue> entry : checkTypeObject(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES, propsObj).entrySet()) {
+ // skip comments
+ if ( entry.getKey().startsWith("#") ) {
+ continue;
+ }
+ checkType("framework property value", entry.getValue(), ValueType.STRING, ValueType.NUMBER, ValueType.TRUE, ValueType.FALSE);
+ if ( container.get(entry.getKey()) != null ) {
+ throw new IOException(this.exceptionPrefix.concat("Duplicate framework property ").concat(entry.getKey()));
+ }
+ final String value = org.apache.felix.cm.json.Configurations.convertToObject( entry.getValue()).toString();
+ container.put(entry.getKey(), value);
+ }
+
+ }
+ }
+
+ private void readExtensions(final JsonObject json,
+ final List<String> keywords,
+ final Extensions container,
+ final Configurations configContainer) throws IOException {
+ final Set<String> keySet = new HashSet<>(json.keySet());
+ keySet.removeAll(keywords);
+ // the remaining keys are considered extensions!
+ for(final String key : keySet) {
+ if ( key.startsWith("#") ) {
+ // skip comments
+ continue;
+ }
+ final int pos = key.indexOf(':');
+ final String postfix = pos == -1 ? null : key.substring(pos + 1);
+ final int sep = (postfix == null ? key.indexOf('|') : postfix.indexOf('|'));
+ final String name;
+ final String type;
+ final String state;
+ if ( pos == -1 ) {
+ type = ExtensionType.ARTIFACTS.name();
+ if ( sep == -1 ) {
+ name = key;
+ state = ExtensionState.OPTIONAL.name();
+ } else {
+ name = key.substring(0, sep);
+ state = key.substring(sep + 1);
+ }
+ } else {
+ name = key.substring(0, pos);
+ if ( sep == -1 ) {
+ type = postfix;
+ state = ExtensionState.OPTIONAL.name();
+ } else {
+ type = postfix.substring(0, sep);
+ state = postfix.substring(sep + 1);
+ }
+ }
+ if ( JSONConstants.FEATURE_KNOWN_PROPERTIES.contains(name) ) {
+ throw new IOException(this.exceptionPrefix.concat("Extension is using reserved name : ").concat(name));
+ }
+ if ( container.getByName(name) != null ) {
+ throw new IOException(exceptionPrefix.concat("Duplicate extension with name ").concat(name));
+ }
+
+ final ExtensionType extType = ExtensionType.valueOf(type);
+ final ExtensionState extState;
+ if (ExtensionState.OPTIONAL.name().equalsIgnoreCase(state)) {
+ extState = ExtensionState.OPTIONAL;
+ } else if (ExtensionState.REQUIRED.name().equalsIgnoreCase(state)) {
+ extState = ExtensionState.REQUIRED;
+ } else if (ExtensionState.TRANSIENT.name().equalsIgnoreCase(state)) {
+ extState = ExtensionState.TRANSIENT;
+ } else {
+ final boolean opt = Boolean.valueOf(state).booleanValue();
+ extState = opt ? ExtensionState.REQUIRED : ExtensionState.OPTIONAL;
+ }
+
+ final Extension ext = new Extension(extType, name, extState);
+ final JsonValue value = json.get(key);
+ switch ( extType ) {
+ case ARTIFACTS : final List<Artifact> list = new ArrayList<>();
+ readArtifacts("Extension ".concat(name), "artifact", list, value, configContainer);
+ for(final Artifact a : list) {
+ if ( ext.getArtifacts().contains(a) ) {
+ throw new IOException(exceptionPrefix.concat("Duplicate artifact in extension ").concat(name).concat(" : ").concat(a.getId().toMvnId()));
+ }
+ ext.getArtifacts().add(a);
+ }
+ break;
+ case JSON : checkType("JSON Extension ".concat(name), value, ValueType.OBJECT, ValueType.ARRAY);
+ ext.setJSONStructure((JsonStructure)value);
+ break;
+ case TEXT : checkType("Text Extension ".concat(name), value, ValueType.STRING, ValueType.ARRAY);
+ if ( value.getValueType() == ValueType.STRING ) {
+ // string
+ final String textValue = org.apache.felix.cm.json.Configurations.convertToObject(value).toString();
+ ext.setText(textValue);
+ } else {
+ // list (array of strings)
+ final StringBuilder sb = new StringBuilder();
+ for(final JsonValue o : ((JsonArray)value)) {
+ final String textValue = checkTypeString("Text Extension ".concat(name).concat(", value ").concat(o.toString()), o);
+ sb.append(textValue);
+ sb.append('\n');
+ }
+ ext.setText(sb.toString());
+ }
+ break;
+ }
+
+ container.add(ext);
+ }
+ }
+
+ /**
+ * Check if the value is one of the provided types
+ * @param key A key for the error message
+ * @param val The value to check
+ * @param types The allowed types, can also include {@code null} if null is an allowed value.
+ * @throws IOException If the val is not of the specified types
+ */
+ private void checkType(final String key, final JsonValue val, ValueType... types) throws IOException {
+ boolean valid = false;
+ for(ValueType t : types) {
+ if (t == null) {
+ if ( val == null) {
+ valid = true;
+ break;
+ }
+ } else if ( val.getValueType() == t ) {
+ valid = true;
+ break;
+ }
+ }
+ if ( !valid ) {
+ throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not one of the allowed types ").concat(Arrays.toString(types)).concat(" : ").concat(val.getValueType().name()));
+ }
+ }
+
+ /**
+ * Check if the value is a string
+ * @param key A key for the error message
+ * @param val The value to check
+ * @return The string value
+ * @throws IOException If the val is not of the specified types
+ */
+ private String checkTypeString(final String key, final JsonValue val) throws IOException {
+ if ( val.getValueType() == ValueType.STRING) {
+ return ((JsonString)val).getString();
+ }
+ throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not of type String : ").concat(val.getValueType().name()));
+ }
+
+ /**
+ * Check if the value is an object
+ * @param key A key for the error message
+ * @param val The value to check
+ * @return The object
+ * @throws IOException If the val is not of the specified types
+ */
+ private JsonObject checkTypeObject(final String key, final JsonValue val) throws IOException {
+ if ( val.getValueType() == ValueType.OBJECT) {
+ return val.asJsonObject();
+ }
+ throw new IOException(this.exceptionPrefix.concat("Key ").concat(key).concat(" is not of type Object : ").concat(val.getValueType().name()));
+ }
+
+ private Prototype readPrototype(final JsonObject json) throws IOException {
+ if ( json.containsKey(JSONConstants.FEATURE_PROTOTYPE)) {
+ final JsonValue prototypeObj = json.get(JSONConstants.FEATURE_PROTOTYPE);
+ checkType(JSONConstants.FEATURE_PROTOTYPE, prototypeObj, ValueType.STRING, ValueType.OBJECT);
+
+ final Prototype prototype;
+ if ( prototypeObj.getValueType() == ValueType.STRING ) {
+ final String textValue = org.apache.felix.cm.json.Configurations.convertToObject(prototypeObj).toString();
+ final ArtifactId id = ArtifactId.parse(textValue);
+ prototype = new Prototype(id);
+ } else {
+ final JsonObject obj = (JsonObject) prototypeObj;
+ if ( !obj.containsKey(JSONConstants.ARTIFACT_ID) ) {
+ throw new IOException(exceptionPrefix.concat(" prototype is missing required artifact id"));
+ }
+ final String textValue = checkTypeString("Prototype ".concat(JSONConstants.ARTIFACT_ID), obj.get(JSONConstants.ARTIFACT_ID));
+ final ArtifactId id = ArtifactId.parse(textValue);
+ prototype = new Prototype(id);
+
+ if ( obj.containsKey(JSONConstants.PROTOTYPE_REMOVALS) ) {
+ final JsonObject removalObj = checkTypeObject("Prototype removals", obj.get(JSONConstants.PROTOTYPE_REMOVALS));
+ if ( removalObj.containsKey(JSONConstants.FEATURE_BUNDLES) ) {
+ checkType("Prototype removal bundles", removalObj.get(JSONConstants.FEATURE_BUNDLES), ValueType.ARRAY);
+ for(final JsonValue val : (JsonArray)removalObj.get(JSONConstants.FEATURE_BUNDLES)) {
+ final String propVal = checkTypeString("Prototype removal bundles", val);
+ if ( propVal.startsWith("#")) {
+ continue;
+ }
+ prototype.getBundleRemovals().add(ArtifactId.parse(propVal));
+ }
+ }
+ if ( removalObj.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
+ checkType("Prototype removal configuration", removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS), ValueType.ARRAY);
+ for(final JsonValue val : (JsonArray)removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS)) {
+ final String propVal = checkTypeString("Prototype removal configuration", val);
+ if ( propVal.startsWith("#") ) {
+ continue;
+ }
+ prototype.getConfigurationRemovals().add(propVal);
+ }
+ }
+ if ( removalObj.containsKey(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES) ) {
+ checkType("Prototype removal framework properties", removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES), ValueType.ARRAY);
+ for(final JsonValue val : (JsonArray)removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES)) {
+ final String propVal = checkTypeString("Prototype removal framework properties", val);
+ if ( propVal.startsWith("#") ) {
+ continue;
+ }
+ prototype.getFrameworkPropertiesRemovals().add(propVal);
+ }
+ }
+ if ( removalObj.containsKey(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS) ) {
+ checkType("Prototype removal extensions", removalObj.get(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS), ValueType.ARRAY);
+ for(final JsonValue val : (JsonArray)removalObj.get(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS)) {
+ checkType("Prototype removal extension", val, ValueType.STRING, ValueType.OBJECT);
+ if ( val.getValueType() == ValueType.STRING ) {
+ final String propVal = org.apache.felix.cm.json.Configurations.convertToObject(val).toString();
+ if ( propVal.startsWith("#")) {
+ continue;
+ }
+ prototype.getExtensionRemovals().add(propVal);
+ } else {
+ final JsonObject removalMap = (JsonObject)val;
+ final JsonValue nameObj = removalMap.get("name");
+ final String name = checkTypeString("Prototype removal extension", nameObj);
+ if ( removalMap.containsKey("artifacts") ) {
+ checkType("Prototype removal extension artifacts", removalMap.get("artifacts"), ValueType.ARRAY);
+ final List<ArtifactId> ids = new ArrayList<>();
+ for(final JsonValue aid : removalMap.getJsonArray("artifacts")) {
+ final String propVal = checkTypeString("Prototype removal extension artifact", aid);
+ if ( propVal.startsWith("#")) {
+ continue;
+ }
+ ids.add(ArtifactId.parse(propVal));
+ }
+ prototype.getArtifactExtensionRemovals().put(name, ids);
+ } else {
+ prototype.getExtensionRemovals().add(name);
+ }
+ }
+ }
+ }
+ readRequirements(removalObj, prototype.getRequirementRemovals());
+ readCapabilities(removalObj, prototype.getCapabilityRemovals());
+
+ }
+ }
+ return prototype;
+ }
+ return null;
+ }
+
+ private void readRequirements(final JsonObject json, final List<MatchingRequirement> container)
+ throws IOException {
+ if ( json.containsKey(JSONConstants.FEATURE_REQUIREMENTS)) {
+ final JsonValue reqObj = json.get(JSONConstants.FEATURE_REQUIREMENTS);
+ checkType(JSONConstants.FEATURE_REQUIREMENTS, reqObj, ValueType.ARRAY);
+
+ for(final JsonValue req : ((JsonArray)reqObj)) {
+ final JsonObject obj = checkTypeObject("Requirement", req);
+
+ if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
+ throw new IOException(this.exceptionPrefix.concat("Namespace is missing for requirement"));
+ }
+ final String namespace = checkTypeString("Requirement namespace", obj.get(JSONConstants.REQCAP_NAMESPACE));
+
+ Map<String, Object> attrMap = new HashMap<>();
+ if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
+ final JsonObject attrs = checkTypeObject("Requirement attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES));
+ attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
+ }
+
+ Map<String, String> dirMap = new HashMap<>();
+ if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
+ final JsonObject dirs = checkTypeObject("Requirement directives", obj.get(JSONConstants.REQCAP_DIRECTIVES));
+ dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
+ }
+
+ final MatchingRequirement r = new MatchingRequirementImpl(null,
+ namespace, dirMap, attrMap);
+ container.add(r);
+ }
+ }
+ }
+
+ private void readCapabilities(final JsonObject json, final List<Capability> container) throws IOException {
+ if ( json.containsKey(JSONConstants.FEATURE_CAPABILITIES)) {
+ final JsonValue capObj = json.get(JSONConstants.FEATURE_CAPABILITIES);
+ checkType(JSONConstants.FEATURE_CAPABILITIES, capObj, ValueType.ARRAY);
+
+ for(final JsonValue cap : ((JsonArray)capObj)) {
+ final JsonObject obj = checkTypeObject("Capability", cap);
+
+ if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
+ throw new IOException(this.exceptionPrefix.concat("Namespace is missing for capability"));
+ }
+ final String namespace = checkTypeString("Capability namespace", obj.get(JSONConstants.REQCAP_NAMESPACE));
+
+ Map<String, Object> attrMap = new HashMap<>();
+ if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
+ final JsonObject attrs = checkTypeObject("Capability attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES));
+ attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
+ }
+
+ Map<String, String> dirMap = new HashMap<>();
+ if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
+ final JsonObject dirs = checkTypeObject("Capability directives", obj.get(JSONConstants.REQCAP_DIRECTIVES));
+ dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
+ }
+
+ final Capability c = new CapabilityImpl(null, namespace, dirMap, attrMap);
+ container.add(c);
+ }
+ }
+ }
+
+ @FunctionalInterface
+ private interface BiConsumer_WithExceptions<T, V, E extends Exception> {
+ void accept(T t, V u) throws E;
+ }
+
+ private static <T, V, E extends Exception> BiConsumer<T, V> rethrowBiConsumer(BiConsumer_WithExceptions<T, V, E> biConsumer) {
+ return (t, u) -> {
+ try {
+ biConsumer.accept(t, u);
+ } catch (Exception exception) {
+ throwAsUnchecked(exception);
+ }
+ };
+ }
+
+ @SuppressWarnings ("unchecked")
+ private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
+ throw (E) exception;
+ }
+
+ private static class MatchingRequirementImpl extends RequirementImpl implements MatchingRequirement {
+
+ public MatchingRequirementImpl(Resource res, String ns, Map<String, String> dirs, Map<String, Object> attrs) {
+ super(res, ns, dirs, attrs);
+ }
}
/**
@@ -69,70 +640,60 @@ public class FeatureJSONReader extends JSONReaderBase {
* @return The feature object
* @throws IOException If an IO error occurs or the JSON is not valid.
*/
- protected Feature readFeature(final Reader reader)
+ private Feature readFeature(final Reader reader)
throws IOException {
- final JsonObject json = Json.createReader(new StringReader(minify(reader))).readObject();
- final Map<String, Object> map = getJsonMap(json);
+ final JsonObject json = Json.createReader(org.apache.felix.cm.json.Configurations.jsonCommentAwareReader(reader)).readObject();
- checkModelVersion(map);
+ checkModelVersion(json);
- final ArtifactId featureId = this.getFeatureId(map);
+ final ArtifactId featureId = this.getFeatureId(json);
this.feature = new Feature(featureId);
this.feature.setLocation(this.location);
// final flag
- if (map.containsKey(JSONConstants.FEATURE_FINAL)) {
- final Object finalObj = map.get(JSONConstants.FEATURE_FINAL);
- checkType(JSONConstants.FEATURE_FINAL, finalObj, Boolean.class);
- this.feature.setFinal(((Boolean) finalObj).booleanValue());
+ if (json.containsKey(JSONConstants.FEATURE_FINAL)) {
+ final JsonValue finalObj = json.get(JSONConstants.FEATURE_FINAL);
+ checkType(JSONConstants.FEATURE_FINAL, finalObj, JsonValue.ValueType.FALSE, JsonValue.ValueType.TRUE);
+ this.feature.setFinal(((Boolean)org.apache.felix.cm.json.Configurations.convertToObject(finalObj)).booleanValue());
}
// complete flag
- if (map.containsKey(JSONConstants.FEATURE_COMPLETE)) {
- final Object completeObj = map.get(JSONConstants.FEATURE_COMPLETE);
- checkType(JSONConstants.FEATURE_COMPLETE, completeObj, Boolean.class);
- this.feature.setComplete(((Boolean) completeObj).booleanValue());
+ if (json.containsKey(JSONConstants.FEATURE_COMPLETE)) {
+ final JsonValue completeObj = json.get(JSONConstants.FEATURE_COMPLETE);
+ checkType(JSONConstants.FEATURE_COMPLETE, completeObj, JsonValue.ValueType.FALSE, JsonValue.ValueType.TRUE);
+ this.feature.setComplete(((Boolean)org.apache.felix.cm.json.Configurations.convertToObject(completeObj)).booleanValue());
}
// title, description, vendor and license
- this.feature.setTitle(getProperty(map, JSONConstants.FEATURE_TITLE));
- this.feature.setDescription(getProperty(map, JSONConstants.FEATURE_DESCRIPTION));
- this.feature.setVendor(getProperty(map, JSONConstants.FEATURE_VENDOR));
- this.feature.setLicense(getProperty(map, JSONConstants.FEATURE_LICENSE));
+ this.feature.setTitle(getProperty(json, JSONConstants.FEATURE_TITLE));
+ this.feature.setDescription(getProperty(json, JSONConstants.FEATURE_DESCRIPTION));
+ this.feature.setVendor(getProperty(json, JSONConstants.FEATURE_VENDOR));
+ this.feature.setLicense(getProperty(json, JSONConstants.FEATURE_LICENSE));
- this.readVariables(map, feature.getVariables());
- this.readBundles(map, feature.getBundles(), feature.getConfigurations());
- this.readFrameworkProperties(map, feature.getFrameworkProperties());
- this.readConfigurations(map, feature.getConfigurations());
+ this.readVariables(json, feature.getVariables());
+ this.readBundles(json, feature.getBundles(), feature.getConfigurations());
+ this.readFrameworkProperties(json, feature.getFrameworkProperties());
+ this.readConfigurations(json, feature.getConfigurations());
- this.readCapabilities(map, feature.getCapabilities());
- this.readRequirements(map, feature.getRequirements());
- feature.setPrototype(this.readPrototype(map));
+ this.readCapabilities(json, feature.getCapabilities());
+ this.readRequirements(json, feature.getRequirements());
+ feature.setPrototype(this.readPrototype(json));
- this.readExtensions(map,
+ this.readExtensions(json,
JSONConstants.FEATURE_KNOWN_PROPERTIES,
this.feature.getExtensions(), this.feature.getConfigurations());
return feature;
}
- private void checkModelVersion(final Map<String, Object> map) throws IOException {
- String modelVersion = getProperty(map, JSONConstants.FEATURE_MODEL_VERSION);
+ private void checkModelVersion(final JsonObject json) throws IOException {
+ String modelVersion = getProperty(json, JSONConstants.FEATURE_MODEL_VERSION);
if (modelVersion == null) {
modelVersion = "1";
}
if (!"1".equals(modelVersion)) {
- throw new IOException("Unsupported model version: " + modelVersion);
- }
- }
-
- protected ArtifactId getFeatureId(final Map<String, Object> map) throws IOException {
- if ( !map.containsKey(JSONConstants.FEATURE_ID) ) {
- throw new IOException(this.exceptionPrefix + "Feature id is missing");
+ throw new IOException(this.exceptionPrefix.concat("Unsupported model version: ").concat(modelVersion));
}
- final Object idObj = map.get(JSONConstants.FEATURE_ID);
- checkType(JSONConstants.FEATURE_ID, idObj, String.class);
- return ArtifactId.parse(idObj.toString());
}
}
diff --git a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
index af36576..029a73b 100644
--- a/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
+++ b/src/main/java/org/apache/sling/feature/io/json/FeatureJSONWriter.java
@@ -16,19 +16,38 @@
*/
package org.apache.sling.feature.io.json;
+import java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import javax.json.Json;
import javax.json.stream.JsonGenerator;
+import javax.json.stream.JsonGeneratorFactory;
+import org.apache.felix.cm.json.ConfigurationResource;
+import org.apache.felix.cm.json.ConfigurationWriter;
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Bundles;
import org.apache.sling.feature.Configuration;
import org.apache.sling.feature.Configurations;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionType;
import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.MatchingRequirement;
+import org.apache.sling.feature.Prototype;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
/**
* Simple JSON writer for a feature
*/
-public class FeatureJSONWriter extends JSONWriterBase {
+public class FeatureJSONWriter {
/**
* Writes the feature to the writer.
@@ -47,6 +66,287 @@ public class FeatureJSONWriter extends JSONWriterBase {
// protected constructor for subclassing
}
+ private final JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true));
+
+ private final JsonGenerator newGenerator(final Writer writer) {
+ // prevent closing of the underlying writer
+ return generatorFactory.createGenerator(new FilterWriter(writer) {
+
+ @Override
+ public void close() throws IOException {
+ super.flush();
+ }
+ });
+ }
+
+ private void writeBundles(final JsonGenerator generator,
+ final Bundles bundles,
+ final Configurations allConfigs) {
+ // bundles
+ if ( !bundles.isEmpty() ) {
+ generator.writeStartArray(JSONConstants.FEATURE_BUNDLES);
+
+ for(final Artifact artifact : bundles) {
+ final Configurations cfgs = new Configurations();
+ for(final Configuration cfg : allConfigs) {
+ final String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID);
+ if ( artifact.getId().toMvnId().equals(artifactProp) ) {
+ cfgs.add(cfg);
+ }
+ }
+ Map<String,String> md = artifact.getMetadata();
+ if ( md.isEmpty() && cfgs.isEmpty() ) {
+ generator.write(artifact.getId().toMvnId());
+ } else {
+ generator.writeStartObject();
+ generator.write(JSONConstants.ARTIFACT_ID, artifact.getId().toMvnId());
+
+ Object runmodes = md.remove("runmodes");
+ if (runmodes instanceof String) {
+ md.put("run-modes", (String) runmodes);
+ }
+
+ for(final Map.Entry<String, String> me : md.entrySet()) {
+ generator.write(me.getKey(), me.getValue());
+ }
+
+ generator.writeEnd();
+ }
+ }
+
+ generator.writeEnd();
+ }
+ }
+
+ /**
+ * Write the list of configurations into a "configurations" element
+ * @param generator The json generator
+ * @param cfgs The list of configurations
+ * @throws IOException
+ */
+ private void writeConfigurations(final JsonGenerator generator, final Configurations cfgs) throws IOException {
+ if ( cfgs.isEmpty() ) {
+ return;
+ }
+
+ generator.writeKey(JSONConstants.FEATURE_CONFIGURATIONS);
+
+ final ConfigurationWriter cfgWriter = org.apache.felix.cm.json.Configurations
+ .buildWriter()
+ .build(generator);
+
+ final ConfigurationResource rsrc = new ConfigurationResource();
+ for(final Configuration cfg : cfgs) {
+ final Hashtable<String, Object> properties;
+ if ( cfg.getProperties() instanceof Hashtable ) {
+ properties = (Hashtable<String, Object>)cfg.getProperties();
+ } else {
+ properties = org.apache.felix.cm.json.Configurations.newConfiguration();
+ for(final String name : Collections.list(cfg.getProperties().keys()) ) {
+ properties.put(name, cfg.getProperties().get(name));
+ }
+ }
+ rsrc.getConfigurations().put(cfg.getPid(), properties);
+ }
+ cfgWriter.writeConfigurationResource(rsrc);
+ }
+
+ private void writeVariables(final JsonGenerator generator, final Map<String,String> vars) {
+ if ( !vars.isEmpty()) {
+ generator.writeStartObject(JSONConstants.FEATURE_VARIABLES);
+
+ for (final Map.Entry<String, String> entry : vars.entrySet()) {
+ String val = entry.getValue();
+ if (val != null)
+ generator.write(entry.getKey(), val);
+ else
+ generator.writeNull(entry.getKey());
+ }
+
+ generator.writeEnd();
+ }
+ }
+
+ private void writeFrameworkProperties(final JsonGenerator generator, final Map<String,String> props) {
+ // framework properties
+ if ( !props.isEmpty() ) {
+ generator.writeStartObject(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
+ for(final Map.Entry<String, String> entry : props.entrySet()) {
+ generator.write(entry.getKey(), entry.getValue());
+ }
+ generator.writeEnd();
+ }
+ }
+
+ private void writeExtensions(final JsonGenerator generator,
+ final List<Extension> extensions,
+ final Configurations allConfigs) throws IOException {
+ for(final Extension ext : extensions) {
+ final String state;
+ switch (ext.getState()) {
+ case OPTIONAL:
+ state = "false";
+ break;
+ case REQUIRED:
+ state = "true";
+ break;
+ default:
+ state = ext.getState().name();
+ }
+ final String key = ext.getName().concat(":").concat(ext.getType().name()).concat("|").concat(state);
+ if ( ext.getType() == ExtensionType.JSON ) {
+ generator.write(key, ext.getJSONStructure());
+ } else if ( ext.getType() == ExtensionType.TEXT ) {
+ generator.writeStartArray(key);
+ for(String line : ext.getText().split("\n")) {
+ generator.write(line);
+ }
+ generator.writeEnd();
+ } else {
+ generator.writeStartArray(key);
+ for(final Artifact artifact : ext.getArtifacts()) {
+ final Configurations artifactCfgs = new Configurations();
+ for(final Configuration cfg : allConfigs) {
+ final String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID);
+ if ( artifact.getId().toMvnId().equals(artifactProp) ) {
+ artifactCfgs.add(cfg);
+ }
+ }
+ if ( artifact.getMetadata().isEmpty() && artifactCfgs.isEmpty() ) {
+ generator.write(artifact.getId().toMvnId());
+ } else {
+ generator.writeStartObject();
+ generator.write(JSONConstants.ARTIFACT_ID, artifact.getId().toMvnId());
+
+ for(final Map.Entry<String, String> me : artifact.getMetadata().entrySet()) {
+ generator.write(me.getKey(), me.getValue());
+ }
+
+ writeConfigurations(generator, artifactCfgs);
+
+ generator.writeEnd();
+ }
+ }
+ generator.writeEnd();
+ }
+ }
+ }
+
+ private void writeProperty(final JsonGenerator generator, final String key, final String value) {
+ if ( value != null ) {
+ generator.write(key, value);
+ }
+ }
+
+ private <T> void writeList(final JsonGenerator generator, final String name, final Collection<T> values) {
+ if (!values.isEmpty()) {
+ generator.writeStartArray(name);
+ for (T value : values) {
+ generator.write(value.toString());
+ }
+ generator.writeEnd();
+ }
+ }
+
+ private void writePrototype(final JsonGenerator generator, final Prototype inc) {
+ if (inc == null) {
+ return;
+ }
+
+ if ( inc.getArtifactExtensionRemovals().isEmpty()
+ && inc.getBundleRemovals().isEmpty()
+ && inc.getConfigurationRemovals().isEmpty()
+ && inc.getFrameworkPropertiesRemovals().isEmpty()
+ && inc.getRequirementRemovals().isEmpty()
+ && inc.getCapabilityRemovals().isEmpty() ) {
+
+ generator.write(JSONConstants.FEATURE_PROTOTYPE, inc.getId().toMvnId());
+ } else {
+ generator.writeStartObject(JSONConstants.FEATURE_PROTOTYPE);
+ writeProperty(generator, JSONConstants.ARTIFACT_ID, inc.getId().toMvnId());
+
+ generator.writeStartObject(JSONConstants.PROTOTYPE_REMOVALS);
+
+ if ( !inc.getArtifactExtensionRemovals().isEmpty()
+ || inc.getExtensionRemovals().isEmpty() ) {
+ generator.writeStartArray(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS);
+
+ for(final String id : inc.getExtensionRemovals()) {
+ generator.write(id);
+ }
+ for(final Map.Entry<String, List<ArtifactId>> entry : inc.getArtifactExtensionRemovals().entrySet()) {
+ generator.writeStartObject();
+
+ writeList(generator, entry.getKey(), entry.getValue());
+
+ generator.writeEnd();
+ }
+
+ generator.writeEnd();
+ }
+ writeList(generator, JSONConstants.FEATURE_CONFIGURATIONS, inc.getConfigurationRemovals());
+ writeList(generator, JSONConstants.FEATURE_BUNDLES, inc.getBundleRemovals());
+ writeList(generator, JSONConstants.FEATURE_FRAMEWORK_PROPERTIES, inc.getFrameworkPropertiesRemovals());
+
+ writeRequirements(generator, inc.getRequirementRemovals());
+ writeCapabilities(generator, inc.getCapabilityRemovals());
+
+ generator.writeEnd().writeEnd();
+ }
+ }
+
+ private void writeRequirements(final JsonGenerator generator, final List<MatchingRequirement> requirements) {
+ if (requirements.isEmpty()) {
+ return;
+ }
+
+ generator.writeStartArray(JSONConstants.FEATURE_REQUIREMENTS);
+
+ for(final Requirement req : requirements) {
+ generator.writeStartObject();
+ writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, req.getNamespace());
+ if ( !req.getAttributes().isEmpty() ) {
+ generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
+ req.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
+ generator.writeEnd();
+ }
+ if ( !req.getDirectives().isEmpty() ) {
+ generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
+ req.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
+ generator.writeEnd();
+ }
+ generator.writeEnd();
+ }
+
+ generator.writeEnd();
+ }
+
+ private void writeCapabilities(final JsonGenerator generator, final List<Capability> capabilities) {
+ if (capabilities.isEmpty()) {
+ return;
+ }
+
+ generator.writeStartArray(JSONConstants.FEATURE_CAPABILITIES);
+
+ for(final Capability cap : capabilities) {
+ generator.writeStartObject();
+ writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, cap.getNamespace());
+ if ( !cap.getAttributes().isEmpty() ) {
+ generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
+ cap.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
+ generator.writeEnd();
+ }
+ if ( !cap.getDirectives().isEmpty() ) {
+ generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
+ cap.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
+ generator.writeEnd();
+ }
+ generator.writeEnd();
+ }
+
+ generator.writeEnd();
+ }
+
/**
* Writes the feature to the writer.
* The writer is not closed.
@@ -54,7 +354,7 @@ public class FeatureJSONWriter extends JSONWriterBase {
* @param feature Feature
* @throws IOException If writing fails
*/
- protected void writeFeature(final Writer writer, final Feature feature)
+ private void writeFeature(final Writer writer, final Feature feature)
throws IOException {
JsonGenerator generator = newGenerator(writer);
generator.writeStartObject();
@@ -113,4 +413,5 @@ public class FeatureJSONWriter extends JSONWriterBase {
final Feature feature) {
writeProperty(generator, JSONConstants.FEATURE_ID, feature.getId().toMvnId());
}
+
}
diff --git a/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java b/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java
deleted file mode 100644
index 679b1f1..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/JSONReaderBase.java
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * 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.json;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.function.BiConsumer;
-
-import javax.json.Json;
-import javax.json.JsonArrayBuilder;
-import javax.json.JsonObject;
-import javax.json.JsonObjectBuilder;
-import javax.json.JsonStructure;
-
-import org.apache.felix.configurator.impl.json.JSMin;
-import org.apache.felix.configurator.impl.json.JSONUtil;
-import org.apache.felix.configurator.impl.json.TypeConverter;
-import org.apache.felix.configurator.impl.model.Config;
-import org.apache.felix.utils.resource.CapabilityImpl;
-import org.apache.felix.utils.resource.RequirementImpl;
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Bundles;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Configurations;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionState;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Extensions;
-import org.apache.sling.feature.MatchingRequirement;
-import org.apache.sling.feature.Prototype;
-import org.osgi.resource.Capability;
-import org.osgi.resource.Resource;
-
-/**
- * Common methods for JSON reading.
- */
-abstract class JSONReaderBase {
-
- /** The optional location. */
- protected final String location;
-
- /** Exception prefix containing the location (if set) */
- protected final String exceptionPrefix;
-
- /**
- * Private constructor
- * @param location Optional location
- */
- JSONReaderBase(final String location) {
- this.location = location;
- if ( location == null ) {
- exceptionPrefix = "";
- } else {
- exceptionPrefix = location + " : ";
- }
- }
-
- protected String minify(final Reader reader) throws IOException {
- // minify JSON (remove comments)
- final String contents;
- try ( final Writer out = new StringWriter()) {
- final JSMin min = new JSMin(reader, out);
- min.jsmin();
- contents = out.toString();
- }
- return contents;
- }
-
- /**
- * Get the JSON object as a map, removing all comments that start with a '#' character
- * @param json The JSON object to process
- * @return A map representing the JSON object.
- */
- protected Map<String, Object> getJsonMap(JsonObject json) {
- @SuppressWarnings("unchecked")
- Map<String, Object> m = (Map<String, Object>) JSONUtil.getValue(json);
-
- removeComments(m);
- return m;
- }
-
- private void removeComments(Map<String, Object> m) {
- for(Iterator<Map.Entry<String, Object>> it = m.entrySet().iterator(); it.hasNext(); ) {
- Entry<String, ?> entry = it.next();
- if (entry.getKey().startsWith("#")) {
- it.remove();
- } else if (entry.getValue() instanceof Map) {
- @SuppressWarnings("unchecked")
- Map<String, Object> embedded = (Map<String, Object>) entry.getValue();
- removeComments(embedded);
- } else if (entry.getValue() instanceof Collection) {
- Collection<?> embedded = (Collection<?>) entry.getValue();
- removeComments(embedded);
- }
- }
- }
-
- @SuppressWarnings("unchecked")
- private void removeComments(Collection<?> embedded) {
- for (Object el : embedded) {
- if (el instanceof Collection) {
- removeComments((Collection<?>) el);
- } else if (el instanceof Map) {
- removeComments((Map<String, Object>) el);
- }
- }
- }
-
- protected String getProperty(final Map<String, Object> map, final String key) throws IOException {
- final Object val = map.get(key);
- if ( val != null ) {
- checkType(key, val, String.class);
- return val.toString();
- }
- return null;
- }
-
- /**
- * Read the variables section
- * @param map The map describing the feature or application
- * @param kvMap The variables will be written to this Key Value Map
- * @return The same variables as a normal map
- * @throws IOException If the json is invalid.
- */
- protected Map<String, String> readVariables(Map<String, Object> map, Map<String,String> kvMap) throws IOException {
- HashMap<String, String> variables = new HashMap<>();
-
- if (map.containsKey(JSONConstants.FEATURE_VARIABLES)) {
- final Object variablesObj = map.get(JSONConstants.FEATURE_VARIABLES);
- checkType(JSONConstants.FEATURE_VARIABLES, variablesObj, Map.class);
-
- @SuppressWarnings("unchecked")
- final Map<String, Object> vars = (Map<String, Object>) variablesObj;
- for (final Map.Entry<String, Object> entry : vars.entrySet()) {
- Object val = entry.getValue();
- checkType("variable value", val, String.class, Boolean.class, Number.class, null);
-
- String key = entry.getKey();
- if (kvMap.get(key) != null) {
- throw new IOException(this.exceptionPrefix + "Duplicate variable " + key);
- }
-
- String value = val == null ? null : val.toString();
-
- kvMap.put(key, value);
- variables.put(key, value);
- }
- }
- return variables;
- }
-
-
- /**
- * Read the bundles / start levels section
- * @param map The map describing the feature
- * @param container The bundles container
- * @param configContainer The configurations container
- * @throws IOException If the json is invalid.
- */
- protected void readBundles(
- final Map<String, Object> map,
- final Bundles container,
- final Configurations configContainer) throws IOException {
- if ( map.containsKey(JSONConstants.FEATURE_BUNDLES)) {
- final Object bundlesObj = map.get(JSONConstants.FEATURE_BUNDLES);
- checkType(JSONConstants.FEATURE_BUNDLES, bundlesObj, List.class);
-
- final List<Artifact> list = new ArrayList<>();
- readArtifacts(JSONConstants.FEATURE_BUNDLES, "bundle", list, bundlesObj, configContainer);
-
- for(final Artifact a : list) {
- if ( container.containsExact(a.getId())) {
- throw new IOException(exceptionPrefix + "Duplicate identical bundle " + a.getId().toMvnId());
- }
- try {
- // check start order
- a.getStartOrder();
- } catch ( final IllegalArgumentException nfe) {
- throw new IOException(exceptionPrefix + "Illegal start order '" + a.getMetadata().get(Artifact.KEY_START_ORDER) + "'");
- }
- container.add(a);
- }
- }
- }
-
- protected void readArtifacts(final String section,
- final String artifactType,
- final List<Artifact> artifacts,
- final Object listObj,
- final Configurations container)
- throws IOException {
- checkType(section, listObj, List.class);
- @SuppressWarnings("unchecked")
- final List<Object> list = (List<Object>) listObj;
- for(final Object entry : list) {
- final Artifact artifact;
- checkType(artifactType, entry, Map.class, String.class);
- if ( entry instanceof String ) {
- // skip comments
- if ( entry.toString().startsWith("#") ) {
- continue;
- }
- artifact = new Artifact(ArtifactId.parse((String) entry));
- } else {
- @SuppressWarnings("unchecked")
- final Map<String, Object> bundleObj = (Map<String, Object>) entry;
- if ( !bundleObj.containsKey(JSONConstants.ARTIFACT_ID) ) {
- throw new IOException(exceptionPrefix + " " + artifactType + " is missing required artifact id");
- }
- checkType(artifactType + " " + JSONConstants.ARTIFACT_ID, bundleObj.get(JSONConstants.ARTIFACT_ID), String.class);
- final ArtifactId id = ArtifactId.parse(bundleObj.get(JSONConstants.ARTIFACT_ID).toString());
-
- artifact = new Artifact(id);
- for(final Map.Entry<String, Object> metadataEntry : bundleObj.entrySet()) {
- final String key = metadataEntry.getKey();
- if ( JSONConstants.ARTIFACT_KNOWN_PROPERTIES.contains(key) ) {
- continue;
- }
- checkType(artifactType + " metadata " + key, metadataEntry.getValue(), String.class, Number.class, Boolean.class);
- artifact.getMetadata().put(key, metadataEntry.getValue().toString());
- }
- if ( bundleObj.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
- checkType(artifactType + " configurations", bundleObj.get(JSONConstants.FEATURE_CONFIGURATIONS), Map.class);
- addConfigurations(bundleObj, artifact, container);
- }
- }
- artifacts.add(artifact);
- }
- }
-
- protected void addConfigurations(final Map<String, Object> map,
- final Artifact artifact,
- final Configurations container) throws IOException {
- final JSONUtil.Report report = new JSONUtil.Report();
- @SuppressWarnings("unchecked")
- final List<Config> configs = JSONUtil.readConfigurationsJSON(new TypeConverter(null),
- 0, "", (Map<String, ?>)map.get(JSONConstants.FEATURE_CONFIGURATIONS), report);
- if ( !report.errors.isEmpty() || !report.warnings.isEmpty() ) {
- final StringBuilder builder = new StringBuilder(exceptionPrefix);
- builder.append("Errors in configurations:");
- for(final String w : report.warnings) {
- builder.append("\n");
- builder.append(w);
- }
- for(final String e : report.errors) {
- builder.append("\n");
- builder.append(e);
- }
- throw new IOException(builder.toString());
- }
-
- for(final Config c : configs) {
- final Configuration config = new Configuration(c.getPid());
-
- final Enumeration<String> keyEnum = c.getProperties().keys();
- while ( keyEnum.hasMoreElements() ) {
- final String key = keyEnum.nextElement();
- final Object val = c.getProperties().get(key);
- config.getProperties().put(key, val);
- }
- if ( config.getProperties().get(Configuration.PROP_ARTIFACT_ID) != null ) {
- throw new IOException(exceptionPrefix + "Configuration must not define property " + Configuration.PROP_ARTIFACT_ID);
- }
- if ( artifact != null ) {
- config.getProperties().put(Configuration.PROP_ARTIFACT_ID, artifact.getId().toMvnId());
- }
- for(final Configuration current : container) {
- if ( current.equals(config) ) {
- throw new IOException(exceptionPrefix + "Duplicate configuration " + config);
- }
- }
- container.add(config);
- }
- }
-
-
- protected void readConfigurations(final Map<String, Object> map,
- final Configurations container) throws IOException {
- if ( map.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
- checkType(JSONConstants.FEATURE_CONFIGURATIONS, map.get(JSONConstants.FEATURE_CONFIGURATIONS), Map.class);
- addConfigurations(map, null, container);
- }
- }
-
- protected void readFrameworkProperties(final Map<String, Object> map,
- final Map<String,String> container) throws IOException {
- if ( map.containsKey(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES) ) {
- final Object propsObj= map.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
- checkType(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES, propsObj, Map.class);
-
- @SuppressWarnings("unchecked")
- final Map<String, Object> props = (Map<String, Object>) propsObj;
- for(final Map.Entry<String, Object> entry : props.entrySet()) {
- checkType("framework property value", entry.getValue(), String.class, Boolean.class, Number.class);
- if ( container.get(entry.getKey()) != null ) {
- throw new IOException(this.exceptionPrefix + "Duplicate framework property " + entry.getKey());
- }
- container.put(entry.getKey(), entry.getValue().toString());
- }
-
- }
- }
-
- protected void readExtensions(final Map<String, Object> map,
- final List<String> keywords,
- final Extensions container,
- final Configurations configContainer) throws IOException {
- final Set<String> keySet = new HashSet<>(map.keySet());
- keySet.removeAll(keywords);
- // the remaining keys are considered extensions!
- for(final String key : keySet) {
- final int pos = key.indexOf(':');
- final String postfix = pos == -1 ? null : key.substring(pos + 1);
- final int sep = (postfix == null ? key.indexOf('|') : postfix.indexOf('|'));
- final String name;
- final String type;
- final String state;
- if ( pos == -1 ) {
- type = ExtensionType.ARTIFACTS.name();
- if ( sep == -1 ) {
- name = key;
- state = ExtensionState.OPTIONAL.name();
- } else {
- name = key.substring(0, sep);
- state = key.substring(sep + 1);
- }
- } else {
- name = key.substring(0, pos);
- if ( sep == -1 ) {
- type = postfix;
- state = ExtensionState.OPTIONAL.name();
- } else {
- type = postfix.substring(0, sep);
- state = postfix.substring(sep + 1);
- }
- }
- if ( JSONConstants.FEATURE_KNOWN_PROPERTIES.contains(name) ) {
- throw new IOException(this.exceptionPrefix + "Extension is using reserved name : " + name);
- }
- if ( container.getByName(name) != null ) {
- throw new IOException(exceptionPrefix + "Duplicate extension with name " + name);
- }
-
- final ExtensionType extType = ExtensionType.valueOf(type);
- final ExtensionState extState;
- if (ExtensionState.OPTIONAL.name().equalsIgnoreCase(state)) {
- extState = ExtensionState.OPTIONAL;
- } else if (ExtensionState.REQUIRED.name().equalsIgnoreCase(state)) {
- extState = ExtensionState.REQUIRED;
- } else if (ExtensionState.TRANSIENT.name().equalsIgnoreCase(state)) {
- extState = ExtensionState.TRANSIENT;
- } else {
- final boolean opt = Boolean.valueOf(state).booleanValue();
- extState = opt ? ExtensionState.REQUIRED : ExtensionState.OPTIONAL;
- }
-
- final Extension ext = new Extension(extType, name, extState);
- final Object value = map.get(key);
- switch ( extType ) {
- case ARTIFACTS : final List<Artifact> list = new ArrayList<>();
- readArtifacts("Extension " + name, "artifact", list, value, configContainer);
- for(final Artifact a : list) {
- if ( ext.getArtifacts().contains(a) ) {
- throw new IOException(exceptionPrefix + "Duplicate artifact in extension " + name + " : " + a.getId().toMvnId());
- }
- ext.getArtifacts().add(a);
- }
- break;
- case JSON : checkType("JSON Extension " + name, value, Map.class, List.class);
- final JsonStructure struct = build(value);
- ext.setJSONStructure(struct);
- break;
- case TEXT : checkType("Text Extension " + name, value, String.class, List.class);
- if ( value instanceof String ) {
- // string
- ext.setText(value.toString());
- } else {
- // list (array of strings)
- @SuppressWarnings("unchecked")
- final List<Object> l = (List<Object>)value;
- final StringBuilder sb = new StringBuilder();
- for(final Object o : l) {
- checkType("Text Extension " + name + ", value " + o, o, String.class);
- sb.append(o.toString());
- sb.append('\n');
- }
- ext.setText(sb.toString());
- }
- break;
- }
-
- container.add(ext);
- }
- }
-
- private JsonStructure build(final Object value) {
- if ( value instanceof List ) {
- @SuppressWarnings("unchecked")
- final List<Object> list = (List<Object>)value;
- final JsonArrayBuilder builder = Json.createArrayBuilder();
- for(final Object obj : list) {
- if ( obj instanceof String ) {
- builder.add(obj.toString());
- } else if ( obj instanceof Long ) {
- builder.add((Long)obj);
- } else if ( obj instanceof Double ) {
- builder.add((Double)obj);
- } else if (obj instanceof Boolean ) {
- builder.add((Boolean)obj);
- } else if ( obj instanceof Map ) {
- builder.add(build(obj));
- } else if ( obj instanceof List ) {
- builder.add(build(obj));
- }
-
- }
- return builder.build();
- } else if ( value instanceof Map ) {
- @SuppressWarnings("unchecked")
- final Map<String, Object> map = (Map<String, Object>)value;
- final JsonObjectBuilder builder = Json.createObjectBuilder();
- for(final Map.Entry<String, Object> entry : map.entrySet()) {
- if ( entry.getValue() instanceof String ) {
- builder.add(entry.getKey(), entry.getValue().toString());
- } else if ( entry.getValue() instanceof Long ) {
- builder.add(entry.getKey(), (Long)entry.getValue());
- } else if ( entry.getValue() instanceof Double ) {
- builder.add(entry.getKey(), (Double)entry.getValue());
- } else if ( entry.getValue() instanceof Boolean ) {
- builder.add(entry.getKey(), (Boolean)entry.getValue());
- } else if ( entry.getValue() instanceof Map ) {
- builder.add(entry.getKey(), build(entry.getValue()));
- } else if ( entry.getValue() instanceof List ) {
- builder.add(entry.getKey(), build(entry.getValue()));
- }
- }
- return builder.build();
- }
- return null;
- }
-
- /**
- * Check if the value is one of the provided types
- * @param key A key for the error message
- * @param val The value to check
- * @param types The allowed types, can also include {@code null} if null is an allowed value.
- * @throws IOException If the val is not of the specified types
- */
- protected void checkType(final String key, final Object val, Class<?>...types) throws IOException {
- boolean valid = false;
- for(final Class<?> c : types) {
- if (c == null) {
- if ( val == null) {
- valid = true;
- break;
- }
- } else if ( c.isInstance(val) ) {
- valid = true;
- break;
- }
- }
- if ( !valid ) {
- throw new IOException(this.exceptionPrefix + "Key " + key + " is not one of the allowed types " + Arrays.toString(types) + " : " + val.getClass());
- }
- }
-
- protected Prototype readPrototype(final Map<String, Object> map) throws IOException {
- if ( map.containsKey(JSONConstants.FEATURE_PROTOTYPE)) {
- final Object prototypeObj = map.get(JSONConstants.FEATURE_PROTOTYPE);
- checkType(JSONConstants.FEATURE_PROTOTYPE, prototypeObj, Map.class, String.class);
-
- final Prototype prototype;
- if ( prototypeObj instanceof String ) {
- final ArtifactId id = ArtifactId.parse(prototypeObj.toString());
- prototype = new Prototype(id);
- } else {
- @SuppressWarnings("unchecked")
- final Map<String, Object> obj = (Map<String, Object>) prototypeObj;
- if ( !obj.containsKey(JSONConstants.ARTIFACT_ID) ) {
- throw new IOException(exceptionPrefix + " prototype is missing required artifact id");
- }
- checkType("Prototype " + JSONConstants.ARTIFACT_ID, obj.get(JSONConstants.ARTIFACT_ID), String.class);
- final ArtifactId id = ArtifactId.parse(obj.get(JSONConstants.ARTIFACT_ID).toString());
- prototype = new Prototype(id);
-
- if ( obj.containsKey(JSONConstants.PROTOTYPE_REMOVALS) ) {
- checkType("Prototype removals", obj.get(JSONConstants.PROTOTYPE_REMOVALS), Map.class);
- @SuppressWarnings("unchecked")
- final Map<String, Object> removalObj = (Map<String, Object>) obj.get(JSONConstants.PROTOTYPE_REMOVALS);
- if ( removalObj.containsKey(JSONConstants.FEATURE_BUNDLES) ) {
- checkType("Prototype removal bundles", removalObj.get(JSONConstants.FEATURE_BUNDLES), List.class);
- @SuppressWarnings("unchecked")
- final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_BUNDLES);
- for(final Object val : list) {
- checkType("Prototype removal bundles", val, String.class);
- if ( val.toString().startsWith("#")) {
- continue;
- }
- prototype.getBundleRemovals().add(ArtifactId.parse(val.toString()));
- }
- }
- if ( removalObj.containsKey(JSONConstants.FEATURE_CONFIGURATIONS) ) {
- checkType("Prototype removal configuration", removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS), List.class);
- @SuppressWarnings("unchecked")
- final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_CONFIGURATIONS);
- for(final Object val : list) {
- checkType("Prototype removal configuration", val, String.class);
- prototype.getConfigurationRemovals().add(val.toString());
- }
- }
- if ( removalObj.containsKey(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES) ) {
- checkType("Prototype removal framework properties", removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES), List.class);
- @SuppressWarnings("unchecked")
- final List<Object> list = (List<Object>)removalObj.get(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
- for(final Object val : list) {
- checkType("Prototype removal framework properties", val, String.class);
- prototype.getFrameworkPropertiesRemovals().add(val.toString());
- }
- }
- if ( removalObj.containsKey(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS) ) {
- checkType("Prototype removal extensions", removalObj.get(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS), List.class);
- @SuppressWarnings("unchecked")
- final List<Object> list = (List<Object>)removalObj.get(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS);
- for(final Object val : list) {
- checkType("Prototype removal extension", val, String.class, Map.class);
- if ( val instanceof String ) {
- if ( val.toString().startsWith("#")) {
- continue;
- }
- prototype.getExtensionRemovals().add(val.toString());
- } else {
- @SuppressWarnings("unchecked")
- final Map<String, Object> removalMap = (Map<String, Object>)val;
- final Object nameObj = removalMap.get("name");
- checkType("Prototype removal extension", nameObj, String.class);
- if ( removalMap.containsKey("artifacts") ) {
- checkType("Prototype removal extension artifacts", removalMap.get("artifacts"), List.class);
- @SuppressWarnings("unchecked")
- final List<Object> artifactList = (List<Object>)removalMap.get("artifacts");
- final List<ArtifactId> ids = new ArrayList<>();
- for(final Object aid : artifactList) {
- checkType("Prototype removal extension artifact", aid, String.class);
- ids.add(ArtifactId.parse(aid.toString()));
- }
- prototype.getArtifactExtensionRemovals().put(nameObj.toString(), ids);
- } else {
- prototype.getExtensionRemovals().add(nameObj.toString());
- }
- }
- }
- }
- readRequirements(removalObj, prototype.getRequirementRemovals());
- readCapabilities(removalObj, prototype.getCapabilityRemovals());
-
- }
- }
- return prototype;
- }
- return null;
- }
-
- protected void readRequirements(Map<String, Object> map, final List<MatchingRequirement> container)
- throws IOException {
- if ( map.containsKey(JSONConstants.FEATURE_REQUIREMENTS)) {
- final Object reqObj = map.get(JSONConstants.FEATURE_REQUIREMENTS);
- checkType(JSONConstants.FEATURE_REQUIREMENTS, reqObj, List.class);
-
- @SuppressWarnings("unchecked")
- final List<Object> requirements = (List<Object>)reqObj;
- for(final Object req : requirements) {
- checkType("Requirement", req, Map.class);
- @SuppressWarnings("unchecked")
- final Map<String, Object> obj = (Map<String, Object>) req;
-
- if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
- throw new IOException(this.exceptionPrefix + "Namespace is missing for requirement");
- }
- checkType("Requirement namespace", obj.get(JSONConstants.REQCAP_NAMESPACE), String.class);
-
- Map<String, Object> attrMap = new HashMap<>();
- if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
- checkType("Requirement attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
- @SuppressWarnings("unchecked")
- final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
- attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
- }
-
- Map<String, String> dirMap = new HashMap<>();
- if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
- checkType("Requirement directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
- @SuppressWarnings("unchecked")
- final Map<String, Object> dirs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_DIRECTIVES);
- dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
- }
-
- final MatchingRequirement r = new MatchingRequirementImpl(null,
- obj.get(JSONConstants.REQCAP_NAMESPACE).toString(), dirMap, attrMap);
- container.add(r);
- }
- }
- }
-
- protected void readCapabilities(Map<String, Object> map, final List<Capability> container) throws IOException {
- if ( map.containsKey(JSONConstants.FEATURE_CAPABILITIES)) {
- final Object capObj = map.get(JSONConstants.FEATURE_CAPABILITIES);
- checkType(JSONConstants.FEATURE_CAPABILITIES, capObj, List.class);
-
- @SuppressWarnings("unchecked")
- final List<Object> capabilities = (List<Object>)capObj;
- for(final Object cap : capabilities) {
- checkType("Capability", cap, Map.class);
- @SuppressWarnings("unchecked")
- final Map<String, Object> obj = (Map<String, Object>) cap;
-
- if ( !obj.containsKey(JSONConstants.REQCAP_NAMESPACE) ) {
- throw new IOException(this.exceptionPrefix + "Namespace is missing for capability");
- }
- checkType("Capability namespace", obj.get(JSONConstants.REQCAP_NAMESPACE), String.class);
-
- Map<String, Object> attrMap = new HashMap<>();
- if ( obj.containsKey(JSONConstants.REQCAP_ATTRIBUTES) ) {
- checkType("Capability attributes", obj.get(JSONConstants.REQCAP_ATTRIBUTES), Map.class);
- @SuppressWarnings("unchecked")
- final Map<String, Object> attrs = (Map<String, Object>)obj.get(JSONConstants.REQCAP_ATTRIBUTES);
- attrs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalAttribute(key, value, attrMap::put)));
- }
-
- Map<String, String> dirMap = new HashMap<>();
- if ( obj.containsKey(JSONConstants.REQCAP_DIRECTIVES) ) {
- checkType("Capability directives", obj.get(JSONConstants.REQCAP_DIRECTIVES), Map.class);
- @SuppressWarnings("unchecked")
- final Map<String, Object> dirs = (Map<String, Object>) obj.get(JSONConstants.REQCAP_DIRECTIVES);
- dirs.forEach(rethrowBiConsumer((key, value) -> ManifestUtils.unmarshalDirective(key, value, dirMap::put)));
- }
-
- final Capability c = new CapabilityImpl(null, obj.get(JSONConstants.REQCAP_NAMESPACE).toString(), dirMap, attrMap);
- container.add(c);
- }
- }
- }
-
- @FunctionalInterface
- private interface BiConsumer_WithExceptions<T, V, E extends Exception> {
- void accept(T t, V u) throws E;
- }
-
- private static <T, V, E extends Exception> BiConsumer<T, V> rethrowBiConsumer(BiConsumer_WithExceptions<T, V, E> biConsumer) {
- return (t, u) -> {
- try {
- biConsumer.accept(t, u);
- } catch (Exception exception) {
- throwAsUnchecked(exception);
- }
- };
- }
-
- @SuppressWarnings ("unchecked")
- private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
- throw (E) exception;
- }
-
- private static class MatchingRequirementImpl extends RequirementImpl implements MatchingRequirement {
-
- public MatchingRequirementImpl(Resource res, String ns, Map<String, String> dirs, Map<String, Object> attrs) {
- super(res, ns, dirs, attrs);
- }
- }
-}
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
deleted file mode 100644
index 9d17744..0000000
--- a/src/main/java/org/apache/sling/feature/io/json/JSONWriterBase.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * 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.json;
-
-import java.io.Writer;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import javax.json.Json;
-import javax.json.stream.JsonGenerator;
-import javax.json.stream.JsonGeneratorFactory;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Bundles;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.Configurations;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionType;
-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;
-
-/**
- * Common functionality for writing JSON
- */
-abstract class JSONWriterBase {
-
- private final JsonGeneratorFactory generatorFactory = Json.createGeneratorFactory(Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true));
-
- protected final JsonGenerator newGenerator(final Writer writer) {
- // prevent closing of the underlying writer
- Writer closeShieldWriter = new CloseShieldWriter(writer);
- return generatorFactory.createGenerator(closeShieldWriter);
- }
-
- protected void writeBundles(final JsonGenerator generator,
- final Bundles bundles,
- final Configurations allConfigs) {
- // bundles
- if ( !bundles.isEmpty() ) {
- generator.writeStartArray(JSONConstants.FEATURE_BUNDLES);
-
- for(final Artifact artifact : bundles) {
- final Configurations cfgs = new Configurations();
- for(final Configuration cfg : allConfigs) {
- final String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID);
- if ( artifact.getId().toMvnId().equals(artifactProp) ) {
- cfgs.add(cfg);
- }
- }
- Map<String,String> md = artifact.getMetadata();
- if ( md.isEmpty() && cfgs.isEmpty() ) {
- generator.write(artifact.getId().toMvnId());
- } else {
- generator.writeStartObject();
- generator.write(JSONConstants.ARTIFACT_ID, artifact.getId().toMvnId());
-
- Object runmodes = md.remove("runmodes");
- if (runmodes instanceof String) {
- md.put("run-modes", (String) runmodes);
- }
-
- for(final Map.Entry<String, String> me : md.entrySet()) {
- generator.write(me.getKey(), me.getValue());
- }
-
- generator.writeEnd();
- }
- }
-
- generator.writeEnd();
- }
- }
-
- /**
- * Write the list of configurations into a "configurations" element
- * @param generator The json generator
- * @param cfgs The list of configurations
- */
- protected void writeConfigurations(final JsonGenerator generator, final Configurations cfgs) {
- if ( cfgs.isEmpty() ) {
- return;
- }
-
- generator.writeStartObject(JSONConstants.FEATURE_CONFIGURATIONS);
-
- for(final Configuration cfg : cfgs) {
- generator.writeStartObject(cfg.getPid());
- ConfiguratorUtil.writeConfiguration(generator, cfg.getConfigurationProperties());
- generator.writeEnd();
- }
-
- generator.writeEnd();
- }
-
- protected void writeVariables(final JsonGenerator generator, final Map<String,String> vars) {
- if ( !vars.isEmpty()) {
- generator.writeStartObject(JSONConstants.FEATURE_VARIABLES);
-
- for (final Map.Entry<String, String> entry : vars.entrySet()) {
- String val = entry.getValue();
- if (val != null)
- generator.write(entry.getKey(), val);
- else
- generator.writeNull(entry.getKey());
- }
-
- generator.writeEnd();
- }
- }
-
- protected void writeFrameworkProperties(final JsonGenerator generator, final Map<String,String> props) {
- // framework properties
- if ( !props.isEmpty() ) {
- generator.writeStartObject(JSONConstants.FEATURE_FRAMEWORK_PROPERTIES);
- for(final Map.Entry<String, String> entry : props.entrySet()) {
- generator.write(entry.getKey(), entry.getValue());
- }
- generator.writeEnd();
- }
- }
-
- protected void writeExtensions(final JsonGenerator generator,
- final List<Extension> extensions,
- final Configurations allConfigs) {
- for(final Extension ext : extensions) {
- final String state;
- switch (ext.getState()) {
- case OPTIONAL:
- state = "false";
- break;
- case REQUIRED:
- state = "true";
- break;
- default:
- state = ext.getState().name();
- }
- final String key = ext.getName().concat(":").concat(ext.getType().name()).concat("|").concat(state);
- if ( ext.getType() == ExtensionType.JSON ) {
- generator.write(key, ext.getJSONStructure());
- } else if ( ext.getType() == ExtensionType.TEXT ) {
- generator.writeStartArray(key);
- for(String line : ext.getText().split("\n")) {
- generator.write(line);
- }
- generator.writeEnd();
- } else {
- generator.writeStartArray(key);
- for(final Artifact artifact : ext.getArtifacts()) {
- final Configurations artifactCfgs = new Configurations();
- for(final Configuration cfg : allConfigs) {
- final String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT_ID);
- if ( artifact.getId().toMvnId().equals(artifactProp) ) {
- artifactCfgs.add(cfg);
- }
- }
- if ( artifact.getMetadata().isEmpty() && artifactCfgs.isEmpty() ) {
- generator.write(artifact.getId().toMvnId());
- } else {
- generator.writeStartObject();
- generator.write(JSONConstants.ARTIFACT_ID, artifact.getId().toMvnId());
-
- for(final Map.Entry<String, String> me : artifact.getMetadata().entrySet()) {
- generator.write(me.getKey(), me.getValue());
- }
-
- writeConfigurations(generator, artifactCfgs);
-
- generator.writeEnd();
- }
- }
- generator.writeEnd();
- }
- }
- }
-
- protected void writeProperty(final JsonGenerator generator, final String key, final String value) {
- if ( value != null ) {
- generator.write(key, value);
- }
- }
-
- protected <T> void writeList(final JsonGenerator generator, final String name, final Collection<T> values) {
- if (!values.isEmpty()) {
- generator.writeStartArray(name);
- for (T value : values) {
- generator.write(value.toString());
- }
- generator.writeEnd();
- }
- }
-
- protected void writePrototype(final JsonGenerator generator, final Prototype inc) {
- if (inc == null) {
- return;
- }
-
- if ( inc.getArtifactExtensionRemovals().isEmpty()
- && inc.getBundleRemovals().isEmpty()
- && inc.getConfigurationRemovals().isEmpty()
- && inc.getFrameworkPropertiesRemovals().isEmpty()
- && inc.getRequirementRemovals().isEmpty()
- && inc.getCapabilityRemovals().isEmpty() ) {
-
- generator.write(JSONConstants.FEATURE_PROTOTYPE, inc.getId().toMvnId());
- } else {
- generator.writeStartObject(JSONConstants.FEATURE_PROTOTYPE);
- writeProperty(generator, JSONConstants.ARTIFACT_ID, inc.getId().toMvnId());
-
- generator.writeStartObject(JSONConstants.PROTOTYPE_REMOVALS);
-
- if ( !inc.getArtifactExtensionRemovals().isEmpty()
- || inc.getExtensionRemovals().isEmpty() ) {
- generator.writeStartArray(JSONConstants.PROTOTYPE_EXTENSION_REMOVALS);
-
- for(final String id : inc.getExtensionRemovals()) {
- generator.write(id);
- }
- for(final Map.Entry<String, List<ArtifactId>> entry : inc.getArtifactExtensionRemovals().entrySet()) {
- generator.writeStartObject();
-
- writeList(generator, entry.getKey(), entry.getValue());
-
- generator.writeEnd();
- }
-
- generator.writeEnd();
- }
- writeList(generator, JSONConstants.FEATURE_CONFIGURATIONS, inc.getConfigurationRemovals());
- writeList(generator, JSONConstants.FEATURE_BUNDLES, inc.getBundleRemovals());
- writeList(generator, JSONConstants.FEATURE_FRAMEWORK_PROPERTIES, inc.getFrameworkPropertiesRemovals());
-
- writeRequirements(generator, inc.getRequirementRemovals());
- writeCapabilities(generator, inc.getCapabilityRemovals());
-
- generator.writeEnd().writeEnd();
- }
- }
-
- protected void writeRequirements(final JsonGenerator generator, final List<MatchingRequirement> requirements) {
- if (requirements.isEmpty()) {
- return;
- }
-
- generator.writeStartArray(JSONConstants.FEATURE_REQUIREMENTS);
-
- for(final Requirement req : requirements) {
- generator.writeStartObject();
- writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, req.getNamespace());
- if ( !req.getAttributes().isEmpty() ) {
- generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
- req.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
- generator.writeEnd();
- }
- if ( !req.getDirectives().isEmpty() ) {
- generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
- req.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
- generator.writeEnd();
- }
- generator.writeEnd();
- }
-
- generator.writeEnd();
- }
-
- protected void writeCapabilities(final JsonGenerator generator, final List<Capability> capabilities) {
- if (capabilities.isEmpty()) {
- return;
- }
-
- generator.writeStartArray(JSONConstants.FEATURE_CAPABILITIES);
-
- for(final Capability cap : capabilities) {
- generator.writeStartObject();
- writeProperty(generator, JSONConstants.REQCAP_NAMESPACE, cap.getNamespace());
- if ( !cap.getAttributes().isEmpty() ) {
- generator.writeStartObject(JSONConstants.REQCAP_ATTRIBUTES);
- cap.getAttributes().forEach((key, value) -> ManifestUtils.marshalAttribute(key, value, generator::write));
- generator.writeEnd();
- }
- if ( !cap.getDirectives().isEmpty() ) {
- generator.writeStartObject(JSONConstants.REQCAP_DIRECTIVES);
- cap.getDirectives().forEach((key, value) -> ManifestUtils.marshalDirective(key, value, generator::write));
- generator.writeEnd();
- }
- generator.writeEnd();
- }
-
- generator.writeEnd();
- }
-}
diff --git a/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java b/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java
index ee6585f..dae75c8 100644
--- a/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java
+++ b/src/main/java/org/apache/sling/feature/io/json/ManifestUtils.java
@@ -16,11 +16,6 @@
*/
package org.apache.sling.feature.io.json;
-import org.apache.felix.utils.resource.CapabilityImpl;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Version;
-import org.osgi.resource.Capability;
-
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -31,21 +26,29 @@ import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
+import javax.json.JsonValue;
+
+import org.apache.felix.cm.json.Configurations;
+import org.apache.felix.utils.resource.CapabilityImpl;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Version;
+import org.osgi.resource.Capability;
+
// This class can be picked up from Felix Utils once it has been moved there. At that point
// this class can be removed.
class ManifestUtils {
- public static void unmarshalAttribute(String key, Object value, BiConsumer<String, Object> sink) throws IOException {
- unmarshal(key + "=" + value, Capability::getAttributes, sink);
+ public static void unmarshalAttribute(String key, JsonValue value, BiConsumer<String, Object> sink) throws IOException {
+ unmarshal(key.concat("=").concat(Configurations.convertToObject(value).toString()), Capability::getAttributes, sink);
}
- public static void unmarshalDirective(String key, Object value, BiConsumer<String, String> sink) throws IOException {
- unmarshal(key + ":=" + value, Capability::getDirectives, sink);
+ public static void unmarshalDirective(String key, JsonValue value, BiConsumer<String, String> sink) throws IOException {
+ unmarshal(key.concat(":=").concat(Configurations.convertToObject(value).toString()), Capability::getDirectives, sink);
}
private static <T> void unmarshal(String header, Function<Capability, Map<String, T>> lookup, BiConsumer<String, T> sink) throws IOException {
try {
convertProvideCapabilities(
- normalizeCapabilityClauses(parseStandardHeader("foo;" + header), "2"))
+ normalizeCapabilityClauses(parseStandardHeader("foo;".concat(header)), "2"))
.forEach(capability -> lookup.apply(capability).forEach(sink));
} catch (Exception e) {
throw new IOException(e);
diff --git a/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java b/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java
index 46b6ba8..4ad71eb 100644
--- a/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java
+++ b/src/test/java/org/apache/sling/feature/io/ConfiguratorUtilTest.java
@@ -17,20 +17,15 @@
package org.apache.sling.feature.io;
import java.io.IOException;
+import java.io.StringReader;
import java.io.StringWriter;
-import java.net.MalformedURLException;
-import java.net.URL;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import java.util.Objects;
-import org.apache.commons.lang3.StringUtils;
-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.apache.felix.cm.json.Configurations;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.hamcrest.core.Every;
@@ -86,22 +81,12 @@ public class ConfiguratorUtilTest {
assertConfigurationJson(writer.toString(), props);
}
- protected void assertConfigurationJson(String json, Dictionary<String, Object> expectedProps) throws MalformedURLException {
- final JSONUtil.Report report = new JSONUtil.Report();
- StringBuilder sb = new StringBuilder("{ \"");
- sb.append("myid");
- sb.append("\" : ");
- sb.append(json);
- sb.append("}");
- final ConfigurationFile configurationFile = JSONUtil.readJSON(new TypeConverter(null),"name", new URL("file:///configtest"),
- 0, sb.toString(), report);
- if ( !report.errors.isEmpty() || !report.warnings.isEmpty() ) {
- Assert.fail("JSON is not the right format: \nErrors: " + StringUtils.join(report.errors) + "\nWarnings: " + StringUtils.join(report.warnings));
- }
+ protected void assertConfigurationJson(String json, Dictionary<String, Object> expectedProps) throws IOException {
+ final Hashtable<String, Object> readProps = Configurations.buildReader().verifyAsBundleResource(true).build(new StringReader(json)).readConfiguration();
// convert to maps for easier comparison
Converter converter = Converters.standardConverter();
Map<String, Object> expectedPropsMap = converter.convert(expectedProps).to(new TypeReference<Map<String,Object>>(){});
- Map<String, Object> actualPropsMap = converter.convert(configurationFile.getConfigurations().get(0).getProperties()).to(new TypeReference<Map<String,Object>>(){});
+ Map<String, Object> actualPropsMap = converter.convert(readProps).to(new TypeReference<Map<String,Object>>(){});
Assert.assertThat(actualPropsMap.entrySet(), Every.everyItem(new MapEntryMatcher<>(expectedPropsMap)));
}
@@ -127,7 +112,7 @@ public class ConfiguratorUtilTest {
boolean isEqual;
if (item.getValue().getClass().isArray()) {
isEqual = Objects.deepEquals(expectedMap.get(item.getKey()), item.getValue());
-
+
} else {
isEqual = expectedMap.get(item.getKey()).equals(item.getValue());
}
diff --git a/src/test/java/org/apache/sling/feature/io/IOUtilsTest.java b/src/test/java/org/apache/sling/feature/io/IOUtilsTest.java
index 4c72e0f..08920a4 100644
--- a/src/test/java/org/apache/sling/feature/io/IOUtilsTest.java
+++ b/src/test/java/org/apache/sling/feature/io/IOUtilsTest.java
@@ -66,97 +66,105 @@ public class IOUtilsTest {
}
@Test public void testGetFileFromURL() throws IOException {
- File file = File.createTempFile("IOUtilsTest \\\\+%23öäü^^^°$::", ".test");
+ File file = File.createTempFile("IOUtilsTest", ".test");
- try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) {
- writer.println("Hello");
- }
-
- assertEquals(file, IOUtils.getFileFromURL(new URL("file:" + file.getPath()), false, null));
-
- assertEquals(file, IOUtils.getFileFromURL(file.toURI().toURL(), false, null));
-
- URL url = new URL(null,"bla:" + file.toURI().toURL(), new URLStreamHandler() {
- @Override
- protected URLConnection openConnection(URL u){
- return new URLConnection(u) {
- @Override
- public void connect()
- {
-
- }
-
- @Override
- public InputStream getInputStream() throws IOException
- {
- return new FileInputStream(file);
- }
- };
+ try {
+ try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) {
+ writer.println("Hello");
}
- });
-
- assertNull(IOUtils.getFileFromURL(url, false, null));
- File tmp = IOUtils.getFileFromURL(url, true, null);
- assertNotEquals(file, tmp);
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(tmp), "UTF-8"))) {
- assertEquals("Hello", reader.readLine());
+ assertEquals(file, IOUtils.getFileFromURL(file.toURI().toURL(), false, null));
+
+ URL url = new URL(null,"bla:" + file.toURI().toURL(), new URLStreamHandler() {
+ @Override
+ protected URLConnection openConnection(URL u){
+ return new URLConnection(u) {
+ @Override
+ public void connect()
+ {
+
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException
+ {
+ return new FileInputStream(file);
+ }
+ };
+ }
+ });
+
+ assertNull(IOUtils.getFileFromURL(url, false, null));
+ File tmp = IOUtils.getFileFromURL(url, true, null);
+
+ assertNotEquals(file, tmp);
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(tmp), "UTF-8"))) {
+ assertEquals("Hello", reader.readLine());
+ }
+ } finally {
+ file.delete();
}
+ File jarFile = File.createTempFile("IOUtilsTes", ".jar");
+ try {
- File jarFile = File.createTempFile("\"IOUtilsTest \\\\\\\\+%23öäü^^^°$::\"", ".jar");
+ try (JarOutputStream output = new JarOutputStream(new FileOutputStream(jarFile))) {
+ output.putNextEntry(new JarEntry("test"));
+ output.write("Hello".getBytes());
+ output.closeEntry();
+ }
- try (JarOutputStream output = new JarOutputStream(new FileOutputStream(jarFile))) {
- output.putNextEntry(new JarEntry("test"));
- output.write("Hello".getBytes());
- output.closeEntry();
+ assertEquals(jarFile, IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/"), false, null));
+ assertNull(IOUtils.getFileFromURL(new URL("jar:file:" + jarFile.getPath() + "!/test"), false, null));
+ File tmpJar = IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), true, null);
+ assertNotNull(tmpJar);
+ assertNotEquals(jarFile, tmpJar);
+ } finally {
+ jarFile.delete();
}
-
- assertEquals(jarFile, IOUtils.getFileFromURL(new URL("jar:file:" + jarFile.getPath() + "!/"), false, null));
- assertEquals(jarFile, IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/"), false, null));
- assertNull(IOUtils.getFileFromURL(new URL("jar:file:" + jarFile.getPath() + "!/test"), false, null));
- File tmpJar = IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), true, null);
- assertNotNull(tmpJar);
- assertNotEquals(jarFile, tmpJar);
}
@Test public void testGetJarFileFromURL() throws IOException {
- File jarFile = File.createTempFile("\"IOUtilsTest \\\\\\\\+%23öäü^^^°$::\"", ".jar");
-
- try (JarOutputStream output = new JarOutputStream(new FileOutputStream(jarFile))) {
- output.putNextEntry(new JarEntry("test"));
- output.write("Hello".getBytes());
- output.closeEntry();
- output.putNextEntry(new JarEntry("test.jar"));
- try (JarOutputStream inner = new JarOutputStream(output)) {
- inner.putNextEntry(new JarEntry("inner"));
- inner.write("Hello".getBytes());
- inner.closeEntry();
+ File jarFile = File.createTempFile("IOUtilsTest", ".jar");
+
+ try {
+ try (JarOutputStream output = new JarOutputStream(new FileOutputStream(jarFile))) {
+ output.putNextEntry(new JarEntry("test"));
+ output.write("Hello".getBytes());
+ output.closeEntry();
+ output.putNextEntry(new JarEntry("test.jar"));
+ try (JarOutputStream inner = new JarOutputStream(output)) {
+ inner.putNextEntry(new JarEntry("inner"));
+ inner.write("Hello".getBytes());
+ inner.closeEntry();
+ }
}
- }
- JarFile jar = IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/"), true, null);
- assertNotNull(jar);
- jar = IOUtils.getJarFileFromURL(jarFile.toURI().toURL(), true, null);
- assertNotNull(jar);
+ JarFile jar = IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/"), true, null);
+ assertNotNull(jar);
+ jar = IOUtils.getJarFileFromURL(jarFile.toURI().toURL(), true, null);
+ assertNotNull(jar);
- assertNull(IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), false, null));
+ assertNull(IOUtils.getFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), false, null));
- JarFile tmpJar = IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test.jar"), true, null);
- assertNotNull(tmpJar);
- assertNotNull(tmpJar.getEntry("inner"));
+ JarFile tmpJar = IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test.jar"), true, null);
+ assertNotNull(tmpJar);
+ assertNotNull(tmpJar.getEntry("inner"));
- try {
- IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), true, null);
- fail();
- } catch (IOException ex) {
- // Expected
- }
+ try {
+ IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test"), true, null);
+ fail();
+ } catch (IOException ex) {
+ // Expected
+ }
- try {
- IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test.jar"), false, null);
- fail();
- } catch (IOException ex) {
- // Expected
+ try {
+ IOUtils.getJarFileFromURL(new URL("jar:" + jarFile.toURI().toURL() + "!/test.jar"), false, null);
+ fail();
+ } catch (IOException ex) {
+ // Expected
+ }
+ } finally {
+ jarFile.delete();
}
}
}
diff --git a/src/test/java/org/apache/sling/feature/io/artifacts/ArtifactManagerTest.java b/src/test/java/org/apache/sling/feature/io/artifacts/ArtifactManagerTest.java
index 5894734..fd106d3 100644
--- a/src/test/java/org/apache/sling/feature/io/artifacts/ArtifactManagerTest.java
+++ b/src/test/java/org/apache/sling/feature/io/artifacts/ArtifactManagerTest.java
@@ -26,9 +26,6 @@ import java.net.URL;
import java.util.HashMap;
import java.util.Map;
-import org.apache.sling.feature.io.artifacts.ArtifactHandler;
-import org.apache.sling.feature.io.artifacts.ArtifactManager;
-import org.apache.sling.feature.io.artifacts.ArtifactManagerConfig;
import org.apache.sling.feature.io.artifacts.spi.ArtifactProvider;
import org.junit.Test;