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 2019/10/17 13:25:54 UTC
[sling-org-apache-sling-feature-extension-apiregions] 01/02:
SLING-8783 : Create API for api regions
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-extension-apiregions.git
commit 864db681c4d301198461a349c57808deff2a5c73
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Thu Oct 17 11:15:10 2019 +0200
SLING-8783 : Create API for api regions
---
pom.xml | 13 +-
.../apiregions/APIRegionMergeHandler.java | 170 +++----------
.../extension/apiregions/AbstractHandler.java | 7 +-
.../apiregions/BundleArtifactFeatureHandler.java | 15 +-
.../extension/apiregions/BundleMappingHandler.java | 16 +-
.../extension/apiregions/api/ApiExport.java | 105 ++++++++
.../extension/apiregions/api/ApiRegion.java | 101 ++++++++
.../extension/apiregions/api/ApiRegions.java | 267 +++++++++++++++++++++
.../extension/apiregions/api/package-info.java | 23 ++
.../apiregions/APIRegionMergeHandlerTest.java | 87 ++++---
.../extension/apiregions/api/TestApiRegions.java | 65 +++++
src/test/resources/json/apis.json | 15 ++
12 files changed, 693 insertions(+), 191 deletions(-)
diff --git a/pom.xml b/pom.xml
index 33d1ecb..0c134eb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
<parent>
<groupId>org.apache.sling</groupId>
<artifactId>sling</artifactId>
- <version>34</version>
+ <version>35</version>
<relativePath />
</parent>
@@ -29,7 +29,7 @@
</scm>
<properties>
- <jdk.version>7</jdk.version>
+ <jdk.version>8</jdk.version>
</properties>
<build>
@@ -48,6 +48,12 @@
</build>
<dependencies>
<dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.annotation.versioning</artifactId>
+ <version>1.0.0</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>
@@ -56,12 +62,13 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.feature</artifactId>
- <version>1.0.4</version>
+ <version>1.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
+ <version>6.0.0</version>
<scope>provided</scope>
</dependency>
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandler.java
index b055807..ec28400 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandler.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandler.java
@@ -16,159 +16,78 @@
*/
package org.apache.sling.feature.extension.apiregions;
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.HandlerContext;
-import org.apache.sling.feature.builder.MergeHandler;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import java.util.Properties;
-import java.util.Set;
import java.util.stream.Collectors;
-import javax.json.Json;
import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.json.JsonReader;
-import javax.json.JsonString;
-import javax.json.JsonValue;
-import javax.json.stream.JsonGenerator;
-import static org.apache.sling.feature.extension.apiregions.AbstractHandler.API_REGIONS_NAME;
-import static org.apache.sling.feature.extension.apiregions.AbstractHandler.EXPORTS_KEY;
-import static org.apache.sling.feature.extension.apiregions.AbstractHandler.NAME_KEY;
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.builder.HandlerContext;
+import org.apache.sling.feature.builder.MergeHandler;
+import org.apache.sling.feature.extension.apiregions.api.ApiExport;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
+/**
+ * Merge to api region extensions
+ */
public class APIRegionMergeHandler implements MergeHandler {
+
@Override
public boolean canMerge(Extension extension) {
- return API_REGIONS_NAME.equals(extension.getName());
+ return ApiRegions.EXTENSION_NAME.equals(extension.getName());
}
@Override
public void merge(HandlerContext context, Feature target, Feature source, Extension targetEx, Extension sourceEx) {
- if (!sourceEx.getName().equals(API_REGIONS_NAME))
+ if (!sourceEx.getName().equals(ApiRegions.EXTENSION_NAME))
return;
- if (targetEx != null && !targetEx.getName().equals(API_REGIONS_NAME))
+ if (targetEx != null && !targetEx.getName().equals(ApiRegions.EXTENSION_NAME))
return;
storeBundleOrigins(context, source, target);
- try (JsonReader srcJR = Json.createReader(new StringReader(sourceEx.getJSON()))) {
- JsonArray srcJA = srcJR.readArray();
-
- Map<String, Map<String, Object>> srcRegions = new LinkedHashMap<>();
- for (int i=0; i < srcJA.size(); i++) {
- String regionName = null;
- Map<String, Object> region = new LinkedHashMap<>();
- JsonObject jo = srcJA.getJsonObject(i);
- for (Map.Entry<String, JsonValue> entry : jo.entrySet()) {
- Object val;
- switch (entry.getKey()) {
- case EXPORTS_KEY:
- val = readJsonArray((JsonArray) entry.getValue());
- break;
- default:
- val = ((JsonString) entry.getValue()).getString();
- if (NAME_KEY.equals(entry.getKey())) {
- regionName = val.toString();
- }
- break;
- }
- region.put(entry.getKey(), val);
- }
- if (regionName == null) {
- throw new IllegalStateException("No region name specified: " + sourceEx.getJSON());
- }
- srcRegions.put(regionName, region);
- }
-
- storeRegionOrigins(context, source, target, srcRegions.keySet());
+ final ApiRegions srcRegions = ApiRegions.parse((JsonArray) sourceEx.getJSONStructure());
- JsonArray tgtJA;
- if (targetEx != null) {
- try (JsonReader tgtJR = Json.createReader(new StringReader(targetEx.getJSON()))) {
- tgtJA = tgtJR.readArray();
- }
- } else {
- targetEx = new Extension(sourceEx.getType(), sourceEx.getName(), sourceEx.isRequired());
- target.getExtensions().add(targetEx);
+ storeRegionOrigins(context, source, target, srcRegions);
- tgtJA = Json.createArrayBuilder().build();
- }
+ final ApiRegions targetRegions;
+ if (targetEx != null) {
+ targetRegions = ApiRegions.parse((JsonArray) targetEx.getJSONStructure());
+ } else {
+ targetEx = new Extension(sourceEx.getType(), sourceEx.getName(), sourceEx.getState());
+ target.getExtensions().add(targetEx);
- StringWriter sw = new StringWriter();
- JsonGenerator gen = Json.createGenerator(sw);
- gen.writeStartArray();
- for (JsonValue jv : tgtJA) {
- if (jv instanceof JsonObject) {
- JsonObject jo = (JsonObject) jv;
- Map<String, Object> srcRegion = srcRegions.remove(jo.getString(NAME_KEY));
- if (srcRegion != null) {
- gen.writeStartObject();
- for (Map.Entry<String, JsonValue> entry : jo.entrySet()) {
- Object sp = srcRegion.get(entry.getKey());
- if (EXPORTS_KEY.equals(entry.getKey()) && sp instanceof List) {
- List<String> tgtPkgs = readJsonArray((JsonArray) entry.getValue());
- @SuppressWarnings("unchecked")
- List<String> srcPkgs = (List<String>) sp;
- for (String srcPkg : srcPkgs) {
- if (!tgtPkgs.contains(srcPkg)) {
- tgtPkgs.add(srcPkg);
- }
- }
- gen.writeStartArray(entry.getKey());
- for (String p : tgtPkgs) {
- gen.write(p);
- }
- gen.writeEnd();
- } else {
- gen.write(entry.getKey(), entry.getValue());
- }
- }
- gen.writeEnd();
- } else {
- gen.write(jv);
- }
- }
- }
+ targetRegions = new ApiRegions();
+ }
- // If there are any remaining regions in the src extension, process them now
- for (Map<String, Object> region : srcRegions.values()) {
- gen.writeStartObject();
- for (Map.Entry<String, Object> entry : region.entrySet()) {
- if (entry.getValue() instanceof Collection) {
- gen.writeStartArray(entry.getKey());
- for (Object o : (Collection<?>) entry.getValue()) {
- gen.write(o.toString());
- }
- gen.writeEnd();
- } else {
- gen.write(entry.getKey(), entry.getValue().toString());
+ for (final ApiRegion targetRegion : targetRegions.getRegions()) {
+ final ApiRegion sourceRegion = srcRegions.getRegionByName(targetRegion.getName());
+ if (sourceRegion != null) {
+ srcRegions.getRegions().remove(sourceRegion);
+ for (final ApiExport srcExp : sourceRegion.getExports()) {
+ if (targetRegion.getExportByName(srcExp.getName()) == null) {
+ targetRegion.getExports().add(srcExp);
}
}
- gen.writeEnd();
}
+ }
- gen.writeEnd();
- gen.close();
+ // If there are any remaining regions in the src extension, process them now
+ targetRegions.getRegions().addAll(srcRegions.getRegions());
- targetEx.setJSON(sw.toString());
- }
+ targetEx.setJSONStructure(targetRegions.toJSONArray());
}
- private void storeRegionOrigins(HandlerContext context, Feature source, Feature target, Set<String> regions) {
+ private void storeRegionOrigins(HandlerContext context, Feature source, Feature target, ApiRegions regions) {
try {
File f = AbstractHandler.getFeatureDataFile(context, target, "regionOrigins.properties");
@@ -180,7 +99,7 @@ public class APIRegionMergeHandler implements MergeHandler {
}
String fid = source.getId().toMvnId();
- p.put(fid, regions.stream().collect(Collectors.joining(",")));
+ p.put(fid, regions.getRegions().stream().map(region -> region.getName()).collect(Collectors.joining(",")));
try (FileOutputStream fos = new FileOutputStream(f)) {
p.store(fos, "Mapping from feature ID to regions that the feature is a member of");
@@ -225,17 +144,4 @@ public class APIRegionMergeHandler implements MergeHandler {
throw new IllegalStateException("Problem storing bundle origin information", e);
}
}
-
- private static List<String> readJsonArray(JsonArray jsonArray) {
- List<String> l = new ArrayList<>();
- for (JsonValue jv : jsonArray) {
- if (jv instanceof JsonString) {
- String pkg = ((JsonString) jv).getString();
- if (!pkg.startsWith("#")) { // ignore comment lines
- l.add(pkg);
- }
- }
- }
- return l;
- }
}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/AbstractHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/AbstractHandler.java
index fd94692..78ddb25 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/AbstractHandler.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/AbstractHandler.java
@@ -16,9 +16,6 @@
*/
package org.apache.sling.feature.extension.apiregions;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.HandlerContext;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -31,8 +28,10 @@ import java.nio.file.Path;
import java.util.Date;
import java.util.Properties;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.builder.HandlerContext;
+
class AbstractHandler {
- static final String API_REGIONS_NAME = "api-regions";
static final String GLOBAL_NAME = "global";
static final String NAME_KEY = "name";
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandler.java
index 967e69e..a3df8ff 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandler.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandler.java
@@ -16,12 +16,6 @@
*/
package org.apache.sling.feature.extension.apiregions;
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.HandlerContext;
-import org.apache.sling.feature.builder.PostProcessHandler;
-
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
@@ -39,10 +33,17 @@ import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonValue;
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.builder.HandlerContext;
+import org.apache.sling.feature.builder.PostProcessHandler;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
+
public class BundleArtifactFeatureHandler extends AbstractHandler implements PostProcessHandler {
@Override
public void postProcess(HandlerContext context, Feature feature, Extension extension) {
- if (!API_REGIONS_NAME.equals(extension.getName()))
+ if (!ApiRegions.EXTENSION_NAME.equals(extension.getName()))
return;
try {
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandler.java
index 4b65678..88ca215 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandler.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandler.java
@@ -16,25 +16,25 @@
*/
package org.apache.sling.feature.extension.apiregions;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Properties;
+import java.util.jar.JarFile;
+
import org.apache.sling.feature.Artifact;
import org.apache.sling.feature.Extension;
import org.apache.sling.feature.Feature;
import org.apache.sling.feature.builder.HandlerContext;
import org.apache.sling.feature.builder.PostProcessHandler;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
import org.apache.sling.feature.io.IOUtils;
import org.osgi.framework.Constants;
-import java.io.File;
-import java.io.IOException;
-import java.net.JarURLConnection;
-import java.net.URL;
-import java.util.Properties;
-import java.util.jar.JarFile;
-
public class BundleMappingHandler extends AbstractHandler implements PostProcessHandler {
@Override
public void postProcess(HandlerContext context, Feature feature, Extension extension) {
- if (!API_REGIONS_NAME.equals(extension.getName()))
+ if (!ApiRegions.EXTENSION_NAME.equals(extension.getName()))
return;
try {
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiExport.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiExport.java
new file mode 100644
index 0000000..2fcbd49
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiExport.java
@@ -0,0 +1,105 @@
+/*
+ * 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.extension.apiregions.api;
+
+import org.apache.sling.feature.ArtifactId;
+
+/**
+ * Describes an exported package.
+ */
+public class ApiExport {
+
+ private volatile String name;
+
+ private volatile String toggle;
+
+ private volatile ArtifactId previous;
+
+ public ApiExport() {
+ // default
+ }
+
+ public ApiExport(final String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getToggle() {
+ return toggle;
+ }
+
+ public void setToggle(String toggle) {
+ this.toggle = toggle;
+ }
+
+ public ArtifactId getPrevious() {
+ return previous;
+ }
+
+ public void setPrevious(ArtifactId previous) {
+ this.previous = previous;
+ }
+
+ @Override
+ public String toString() {
+ return "ApiExport [name=" + name + ", toggle=" + toggle + ", previous=" + previous + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((previous == null) ? 0 : previous.hashCode());
+ result = prime * result + ((toggle == null) ? 0 : toggle.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ApiExport other = (ApiExport) obj;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (previous == null) {
+ if (other.previous != null)
+ return false;
+ } else if (!previous.equals(other.previous))
+ return false;
+ if (toggle == null) {
+ if (other.toggle != null)
+ return false;
+ } else if (!toggle.equals(other.toggle))
+ return false;
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegion.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegion.java
new file mode 100644
index 0000000..d8ef2fa
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegion.java
@@ -0,0 +1,101 @@
+/*
+ * 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.extension.apiregions.api;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * Describes an api region
+ */
+public class ApiRegion {
+
+ private final Collection<ApiExport> exports = new HashSet<>();
+
+ private final Map<String, String> properties = new HashMap<>();
+
+ private volatile String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Collection<ApiExport> getExports() {
+ return this.exports;
+ }
+
+ public ApiExport getExportByName(final String name) {
+ for (final ApiExport e : this.exports) {
+ if (e.getName().equals(name)) {
+ return e;
+ }
+ }
+ return null;
+ }
+
+ public Map<String, String> getProperties() {
+ return this.properties;
+ }
+
+ @Override
+ public String toString() {
+ return "ApiRegion [exports=" + exports + ", properties=" + properties + ", name=" + name + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((exports == null) ? 0 : exports.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ApiRegion other = (ApiRegion) obj;
+ if (exports == null) {
+ if (other.exports != null)
+ return false;
+ } else if (!exports.equals(other.exports))
+ return false;
+ if (name == null) {
+ if (other.name != null)
+ return false;
+ } else if (!name.equals(other.name))
+ return false;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ } else if (!properties.equals(other.properties))
+ return false;
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegions.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegions.java
new file mode 100644
index 0000000..00d939e
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegions.java
@@ -0,0 +1,267 @@
+/*
+ * 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.extension.apiregions.api;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonArrayBuilder;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonReader;
+import javax.json.JsonString;
+import javax.json.JsonValue;
+import javax.json.JsonValue.ValueType;
+import javax.json.JsonWriter;
+
+import org.apache.sling.feature.ArtifactId;
+
+/**
+ * An api regions configuration
+ */
+public class ApiRegions {
+
+ /** The name of the api regions extension. */
+ public static final String EXTENSION_NAME = "api-regions";
+
+ private static final String NAME_KEY = "name";
+
+ private static final String EXPORTS_KEY = "exports";
+
+ private static final String TOGGLE_KEY = "toggle";
+
+ private static final String PREVIOUS_KEY = "previous";
+
+ private final List<ApiRegion> regions = new ArrayList<>();
+
+ public List<ApiRegion> getRegions() {
+ return this.regions;
+ }
+
+ /**
+ * Add the region. The region is only added if there isn't already a region with
+ * the same name
+ *
+ * @param region The region to add
+ * @return {@code true} if the region could be added, {@code false} otherwise
+ */
+ public boolean addUniqueRegion(final ApiRegion region) {
+ boolean found = false;
+ for (final ApiRegion c : this.regions) {
+ if (c.getName().equals(region.getName())) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ this.regions.add(region);
+ }
+ return !found;
+ }
+
+ /**
+ * Get a named region
+ *
+ * @param name The name
+ * @return The region or {@code null}
+ */
+ public ApiRegion getRegionByName(final String name) {
+ ApiRegion found = null;
+
+ for (final ApiRegion c : this.regions) {
+ if (c.getName().equals(name)) {
+ found = c;
+ break;
+ }
+ }
+
+ return found;
+ }
+
+ /**
+ * Get the names of the regions
+ *
+ * @return The list of regions, might be empty
+ */
+ public List<String> getRegionNames() {
+ final List<String> names = new ArrayList<>();
+ for (final ApiRegion c : this.regions) {
+ names.add(c.getName());
+ }
+ return Collections.unmodifiableList(names);
+ }
+
+ /**
+ * Convert regions into json
+ *
+ * @return The json array
+ */
+ public JsonArray toJSONArray() {
+ final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();
+
+ for (final ApiRegion region : this.getRegions()) {
+ final JsonObjectBuilder regionBuilder = Json.createObjectBuilder();
+ regionBuilder.add(NAME_KEY, region.getName());
+
+ if (!region.getExports().isEmpty()) {
+ final JsonArrayBuilder expArrayBuilder = Json.createArrayBuilder();
+ for (final ApiExport exp : region.getExports()) {
+ if (exp.getToggle() == null) {
+ expArrayBuilder.add(exp.getName());
+ } else {
+ final JsonObjectBuilder expBuilder = Json.createObjectBuilder();
+ expBuilder.add(NAME_KEY, exp.getName());
+ expBuilder.add(TOGGLE_KEY, exp.getToggle());
+ if (exp.getPrevious() != null) {
+ expBuilder.add(PREVIOUS_KEY, exp.getPrevious().toMvnId());
+ }
+ expArrayBuilder.add(expBuilder);
+ }
+ }
+
+ regionBuilder.add(EXPORTS_KEY, expArrayBuilder);
+ }
+ for (final Map.Entry<String, String> entry : region.getProperties().entrySet()) {
+ regionBuilder.add(entry.getKey(), entry.getValue());
+ }
+
+ arrayBuilder.add(regionBuilder);
+ }
+
+ return arrayBuilder.build();
+ }
+
+ /**
+ * Convert regions into json
+ *
+ * @return The json array as a string
+ */
+ public String toJSON() {
+ final JsonArray array = this.toJSONArray();
+ try (final StringWriter stringWriter = new StringWriter();
+ final JsonWriter writer = Json.createWriter(stringWriter)) {
+ writer.writeArray(array);
+ return stringWriter.toString();
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * Parse a JSON array into an api regions object
+ *
+ * @param json The json as a string
+ * @return The api regions
+ */
+ public static ApiRegions parse(final String json) {
+ try (final JsonReader reader = Json.createReader(new StringReader(json))) {
+ return parse(reader.readArray());
+ }
+ }
+
+ /**
+ * Parse a JSON array into an api regions object
+ *
+ * @param json The json
+ * @return The api regions
+ */
+ public static ApiRegions parse(final JsonArray json) {
+ final ApiRegions regions = new ApiRegions();
+
+ for (final JsonValue value : json) {
+ if (value.getValueType() != ValueType.OBJECT) {
+ throw new IllegalArgumentException("Illegal api regions json " + json);
+ }
+ final ApiRegion region = new ApiRegion();
+
+ final JsonObject obj = (JsonObject) value;
+ region.setName(obj.getString(NAME_KEY));
+
+ for(final Map.Entry<String, JsonValue> entry : obj.entrySet()) {
+ if ( NAME_KEY.equals(entry.getKey()) ) {
+ region.setName(obj.getString(NAME_KEY));
+ } else if (entry.getKey().equals(EXPORTS_KEY)) {
+ for (final JsonValue e : (JsonArray)entry.getValue()) {
+ if (e.getValueType() == ValueType.STRING) {
+ final String name = ((JsonString) e).getString();
+ if (!name.startsWith("#")) {
+ final ApiExport export = new ApiExport();
+ region.getExports().add(export);
+
+ export.setName(name);
+ }
+ } else if (e.getValueType() == ValueType.OBJECT) {
+ final JsonObject expObj = (JsonObject) e;
+ final ApiExport export = new ApiExport();
+ region.getExports().add(export);
+
+ export.setName(expObj.getString(NAME_KEY));
+ export.setToggle(expObj.getString(TOGGLE_KEY, null));
+ if (expObj.containsKey(PREVIOUS_KEY)) {
+ export.setPrevious(ArtifactId.parse(expObj.getString(PREVIOUS_KEY)));
+ }
+ }
+ }
+ } else {
+ region.getProperties().put(entry.getKey(), ((JsonString) entry.getValue()).getString());
+ }
+ }
+ if (!regions.addUniqueRegion(region)) {
+ throw new IllegalArgumentException("Region " + region.getName() + " is defined twice");
+ }
+
+ }
+ return regions;
+ }
+
+ @Override
+ public String toString() {
+ return "ApiRegions [regions=" + regions + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((regions == null) ? 0 : regions.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ApiRegions other = (ApiRegions) obj;
+ if (regions == null) {
+ if (other.regions != null)
+ return false;
+ } else if (!regions.equals(other.regions))
+ return false;
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/package-info.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/package-info.java
new file mode 100644
index 0000000..72e56f8
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+@org.osgi.annotation.versioning.Version("1.0.0")
+package org.apache.sling.feature.extension.apiregions.api;
+
+
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandlerTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandlerTest.java
index fa965e8..aa15510 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandlerTest.java
@@ -16,16 +16,9 @@
*/
package org.apache.sling.feature.extension.apiregions;
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.HandlerContext;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileInputStream;
@@ -41,9 +34,20 @@ import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonReader;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
+import org.apache.sling.feature.ExtensionType;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.builder.HandlerContext;
+import org.apache.sling.feature.extension.apiregions.api.ApiExport;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
public class APIRegionMergeHandlerTest {
private Path tempDir;
@@ -66,9 +70,9 @@ public class APIRegionMergeHandlerTest {
public void testCanMerge() {
APIRegionMergeHandler armh = new APIRegionMergeHandler();
- Extension ex = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
assertTrue(armh.canMerge(ex));
- assertFalse(armh.canMerge(new Extension(ExtensionType.JSON, "foo", false)));
+ assertFalse(armh.canMerge(new Extension(ExtensionType.JSON, "foo", ExtensionState.OPTIONAL)));
}
@Test
@@ -78,14 +82,14 @@ public class APIRegionMergeHandlerTest {
Feature tf = new Feature(ArtifactId.fromMvnId("x:t:1"));
Feature sf = new Feature(ArtifactId.fromMvnId("y:s:2"));
- Extension tgEx = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension tgEx = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
tgEx.setJSON("[{\"name\":\"global\","
+ "\"exports\": [\"a.b.c\",\"d.e.f\"]},"
+ "{\"name\":\"internal\","
+ "\"exports\":[\"xyz\"],"
+ "\"some-key\":\"some-val\"}]");
- Extension srEx = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension srEx = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
srEx.setJSON("[{\"name\":\"global\","
+ "\"exports\": [\"test\"]},"
+ "{\"name\":\"something\","
@@ -95,20 +99,29 @@ public class APIRegionMergeHandlerTest {
HandlerContext hc = Mockito.mock(HandlerContext.class);
armh.merge(hc, tf, sf, tgEx, srEx);
- String expectedJSON = "[{\"name\":\"global\","
- + "\"exports\": [\"a.b.c\",\"d.e.f\", \"test\"]},"
- + "{\"name\":\"internal\","
- + "\"exports\":[\"xyz\"],"
- + "\"some-key\":\"some-val\"},"
- + "{\"name\":\"something\","
- + "\"exports\": [\"a.ha\"],"
- + "\"my-key\": \"my-val\"}]";
- JsonReader er = Json.createReader(new StringReader(expectedJSON));
- JsonReader ar = Json.createReader(new StringReader(tgEx.getJSON()));
- JsonArray ea = er.readArray();
- JsonArray aa = ar.readArray();
-
- assertEquals(ea, aa);
+ ApiRegions expected = new ApiRegions();
+ ApiRegion global = new ApiRegion();
+ global.setName("global");
+ global.getExports().add(new ApiExport("a.b.c"));
+ global.getExports().add(new ApiExport("d.e.f"));
+ global.getExports().add(new ApiExport("test"));
+ expected.getRegions().add(global);
+
+ ApiRegion internal = new ApiRegion();
+ internal.setName("internal");
+ internal.getExports().add(new ApiExport("xyz"));
+ internal.getProperties().put("some-key", "some-val");
+ expected.getRegions().add(internal);
+
+ ApiRegion something = new ApiRegion();
+ something.setName("something");
+ something.getExports().add(new ApiExport("a.ha"));
+ something.getProperties().put("my-key", "my-val");
+ expected.getRegions().add(something);
+
+ ApiRegions created = ApiRegions.parse((JsonArray) tgEx.getJSONStructure());
+
+ assertEquals(expected, created);
}
@@ -119,7 +132,7 @@ public class APIRegionMergeHandlerTest {
Feature tf = new Feature(ArtifactId.fromMvnId("x:t:1"));
Feature sf = new Feature(ArtifactId.fromMvnId("y:s:2"));
- Extension srEx = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension srEx = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
srEx.setJSON("[{\"name\":\"global\","
+ "\"exports\": [\"a.b.c\",\"d.e.f\"]},"
+ "{\"name\":\"deprecated\","
@@ -157,7 +170,7 @@ public class APIRegionMergeHandlerTest {
Feature tf = new Feature(ArtifactId.fromMvnId("g:t:1"));
Feature sf1 = new Feature(ArtifactId.fromMvnId("g:s1:1"));
- Extension sf1Ex = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension sf1Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
sf1Ex.setJSON("[]");
sf1.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b1:1")));
@@ -166,7 +179,7 @@ public class APIRegionMergeHandlerTest {
armh.merge(hc, tf, sf1, null, sf1Ex);
Feature sf2 = new Feature(ArtifactId.fromMvnId("g:s2:1"));
- Extension sf2Ex = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension sf2Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
sf2Ex.setJSON("[]");
sf2.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b2:1")));
@@ -176,7 +189,7 @@ public class APIRegionMergeHandlerTest {
armh.merge(hc, tf, sf2, tf.getExtensions().getByName("api-regions"), sf2Ex);
Feature sf3 = new Feature(ArtifactId.fromMvnId("g:s3:1"));
- Extension sf3Ex = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension sf3Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
sf3Ex.setJSON("[]");
sf3.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b2:1")));
@@ -204,7 +217,7 @@ public class APIRegionMergeHandlerTest {
Feature tf = new Feature(ArtifactId.fromMvnId("x:t:1"));
Feature sf1 = new Feature(ArtifactId.fromMvnId("y:s:2"));
- Extension sr1Ex = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension sr1Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
sr1Ex.setJSON("[{\"name\":\"global\","
+ "\"exports\": [\"a.b.c\",\"d.e.f\"]},"
+ "{\"name\":\"deprecated\","
@@ -218,7 +231,7 @@ public class APIRegionMergeHandlerTest {
Feature sf2 = new Feature(ArtifactId.fromMvnId("z:s:1"));
- Extension sr2Ex = new Extension(ExtensionType.JSON, "api-regions", false);
+ Extension sr2Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
sr2Ex.setJSON("[{\"name\":\"global\","
+ "\"exports\": [\"g.h.i\"]},"
+ "{\"name\":\"internal\","
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/TestApiRegions.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/TestApiRegions.java
new file mode 100644
index 0000000..a03b959
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/TestApiRegions.java
@@ -0,0 +1,65 @@
+/*
+ * 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.extension.apiregions.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.junit.Test;
+
+public class TestApiRegions {
+
+ private String readJSON(final String name) throws IOException {
+ try (final Reader reader = new InputStreamReader(
+ TestApiRegions.class.getResourceAsStream("/json/" + name + ".json"),
+ "UTF-8"); final Writer writer = new StringWriter()) {
+ int l;
+ char[] buf = new char[2048];
+ while ((l = reader.read(buf)) > -1) {
+ writer.write(buf, 0, l);
+ }
+
+ return writer.toString();
+ }
+ }
+
+ @Test
+ public void testParsing() throws Exception {
+ final String json = readJSON("apis");
+
+ final ApiRegions regions = ApiRegions.parse(json);
+ assertNotNull(regions);
+
+ assertEquals(2, regions.getRegions().size());
+
+ final ApiRegion global = regions.getRegions().get(0);
+ assertEquals("global", global.getName());
+
+ assertEquals(2, global.getExports().size());
+
+ final ApiRegion internal = regions.getRegions().get(1);
+ assertEquals("internal", internal.getName());
+
+ assertEquals(1, internal.getExports().size());
+ }
+}
diff --git a/src/test/resources/json/apis.json b/src/test/resources/json/apis.json
new file mode 100644
index 0000000..eaa81a5
--- /dev/null
+++ b/src/test/resources/json/apis.json
@@ -0,0 +1,15 @@
+[
+ {
+ "name" : "global",
+ "exports" : [
+ "org.apache.sling.global",
+ "org.apache.felix.global"
+ ]
+ },
+ {
+ "name" : "internal",
+ "exports" : [
+ "org.apache.sling.internal"
+ ]
+ }
+]