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/11/13 08:05:07 UTC
[sling-org-apache-sling-feature-extension-apiregions] branch
SLING-9867 updated: Start implementing merge handler, add region support
This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch SLING-9867
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-extension-apiregions.git
The following commit(s) were added to refs/heads/SLING-9867 by this push:
new 803b114 Start implementing merge handler, add region support
803b114 is described below
commit 803b1145ea8bd255158bbe515ce6a5c041e71cdd
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Nov 13 09:04:03 2020 +0100
Start implementing merge handler, add region support
---
.../apiregions/ConfigurationApiMergeHandler.java | 48 +++++++++
.../apiregions/api/config/ConfigurationApi.java | 57 +++++++++-
.../apiregions/api/config/InternalConstants.java | 2 +
.../extension/apiregions/api/config/Region.java | 24 +++++
.../org.apache.sling.feature.builder.MergeHandler | 1 +
.../ConfigurationApiMergeHandlerTest.java | 115 +++++++++++++++++++++
.../api/config/ConfigurationApiTest.java | 27 ++++-
...pertyTest.java => PropertyDescriptionTest.java} | 2 +-
8 files changed, 271 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/ConfigurationApiMergeHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/ConfigurationApiMergeHandler.java
index 7c83c71..b7d6b68 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/ConfigurationApiMergeHandler.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/ConfigurationApiMergeHandler.java
@@ -16,12 +16,18 @@
*/
package org.apache.sling.feature.extension.apiregions;
+import java.util.Map;
+
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.apache.sling.feature.builder.MergeHandler;
import org.apache.sling.feature.extension.apiregions.api.config.ConfigurationApi;
+import org.apache.sling.feature.extension.apiregions.api.config.ConfigurationDescription;
+import org.apache.sling.feature.extension.apiregions.api.config.FactoryConfigurationDescription;
+import org.apache.sling.feature.extension.apiregions.api.config.FrameworkPropertyDescription;
+import org.apache.sling.feature.extension.apiregions.api.config.Region;
/**
* Merge the configuration api extension
@@ -44,9 +50,51 @@ public class ConfigurationApiMergeHandler implements MergeHandler {
// no target available yet, just copy source
final Extension ext = new Extension(ExtensionType.JSON, ConfigurationApi.EXTENSION_NAME, sourceExtension.getState());
ext.setJSON(sourceExtension.getJSON());
+ targetFeature.getExtensions().add(ext);
} else {
final ConfigurationApi sourceApi = ConfigurationApi.getConfigurationApi(sourceExtension);
final ConfigurationApi targetApi = ConfigurationApi.getConfigurationApi(targetExtension);
+
+ // region merging
+ if ( context.isInitialMerge() ) {
+ targetApi.setRegion(sourceApi.getRegion());
+ } else {
+ // region merging is different for prototypes
+ if ( sourceApi.getRegion() != targetApi.getRegion() ) {
+ if ( context.isPrototypeMerge() ) {
+ if ( sourceApi.getRegion() != null ) {
+ targetApi.setRegion(sourceApi.getRegion());
+ }
+ } else {
+ targetApi.setRegion(Region.GLOBAL);
+ }
+ }
+ }
+
+ // merge - but throw on duplicates
+ for(final Map.Entry<String, ConfigurationDescription> entry : sourceApi.getConfigurationDescriptions().entrySet()) {
+ if ( targetApi.getConfigurationDescriptions().containsKey(entry.getKey())) {
+ throw new IllegalStateException("Duplicate configuration description " + entry.getKey());
+ }
+ targetApi.getConfigurationDescriptions().put(entry.getKey(), entry.getValue());
+ }
+ for(final Map.Entry<String, FactoryConfigurationDescription> entry : sourceApi.getFactoryConfigurationDescriptions().entrySet()) {
+ if ( targetApi.getFactoryConfigurationDescriptions().containsKey(entry.getKey())) {
+ throw new IllegalStateException("Duplicate factory configuration description " + entry.getKey());
+ }
+ targetApi.getFactoryConfigurationDescriptions().put(entry.getKey(), entry.getValue());
+ }
+ for(final Map.Entry<String, FrameworkPropertyDescription> entry : sourceApi.getFrameworkPropertyDescriptions().entrySet()) {
+ if ( targetApi.getFrameworkPropertyDescriptions().containsKey(entry.getKey())) {
+ throw new IllegalStateException("Duplicate framework property description " + entry.getKey());
+ }
+ targetApi.getFrameworkPropertyDescriptions().put(entry.getKey(), entry.getValue());
+ }
+ targetApi.getInternalConfigurations().addAll(sourceApi.getInternalConfigurations());
+ targetApi.getInternalFactoryConfigurations().addAll(sourceApi.getInternalFactoryConfigurations());
+ targetApi.getInternalFrameworkProperties().addAll(sourceApi.getInternalFrameworkProperties());
+
+ ConfigurationApi.setConfigurationApi(targetFeature, targetApi);
}
}
}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
index 33599d8..2d7af47 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApi.java
@@ -30,6 +30,7 @@ import javax.json.JsonObjectBuilder;
import javax.json.JsonValue;
import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionState;
import org.apache.sling.feature.ExtensionType;
import org.apache.sling.feature.Feature;
@@ -78,6 +79,32 @@ public class ConfigurationApi extends AttributeableEntity {
}
}
+ /**
+ * Set the configuration api as an extension to the feature
+ * @param feature The feature
+ * @param api The configuration api
+ * @throws IllegalStateException If the feature has already an extension of a wrong type
+ * @throws IllegalArgumentException If the api configuration can't be serialized to JSON
+ */
+ public static void setConfigurationApi(final Feature feature, final ConfigurationApi api) {
+ Extension ext = feature.getExtensions().getByName(EXTENSION_NAME);
+ if ( api == null ) {
+ if ( ext != null ) {
+ feature.getExtensions().remove(ext);
+ }
+ } else {
+ if ( ext == null ) {
+ ext = new Extension(ExtensionType.JSON, EXTENSION_NAME, ExtensionState.OPTIONAL);
+ feature.getExtensions().add(ext);
+ }
+ try {
+ ext.setJSONStructure(api.toJSONObject());
+ } catch ( final IOException ioe) {
+ throw new IllegalArgumentException(ioe);
+ }
+ }
+ }
+
/** The map of configurations */
private final Map<String, ConfigurationDescription> configurations = new LinkedHashMap<>();
@@ -96,6 +123,9 @@ public class ConfigurationApi extends AttributeableEntity {
/** The list of internal framework property names */
private final List<String> internalFrameworkProperties = new ArrayList<>();
+ /** The configuration region of this feature */
+ private Region region;
+
/**
* Clear the object and reset to defaults
*/
@@ -107,6 +137,7 @@ public class ConfigurationApi extends AttributeableEntity {
this.internalConfigurations.clear();
this.internalFactories.clear();
this.internalFrameworkProperties.clear();
+ this.setRegion(null);
}
/**
@@ -119,6 +150,11 @@ public class ConfigurationApi extends AttributeableEntity {
public void fromJSONObject(final JsonObject jsonObj) throws IOException {
super.fromJSONObject(jsonObj);
try {
+ final String typeVal = this.getString(InternalConstants.KEY_REGION);
+ if ( typeVal != null ) {
+ this.setRegion(Region.valueOf(typeVal.toUpperCase()));
+ }
+
JsonValue val;
val = this.getAttributes().remove(InternalConstants.KEY_CONFIGURATIONS);
if ( val != null ) {
@@ -222,6 +258,22 @@ public class ConfigurationApi extends AttributeableEntity {
}
/**
+ * Get the api configuration region
+ * @return The region or {@code null}
+ */
+ public Region getRegion() {
+ return this.region;
+ }
+
+ /**
+ * Set the api configuration region
+ * @param value The region to set
+ */
+ public void setRegion(final Region value) {
+ this.region = value;
+ }
+
+ /**
* Convert this object into JSON
*
* @return The json object builder
@@ -229,6 +281,9 @@ public class ConfigurationApi extends AttributeableEntity {
*/
JsonObjectBuilder createJson() throws IOException {
final JsonObjectBuilder objBuilder = super.createJson();
+ if ( this.getRegion() != null ) {
+ objBuilder.add(InternalConstants.KEY_REGION, this.getRegion().name());
+ }
if ( !this.getConfigurationDescriptions().isEmpty() ) {
final JsonObjectBuilder propBuilder = Json.createObjectBuilder();
for(final Map.Entry<String, ConfigurationDescription> entry : this.getConfigurationDescriptions().entrySet()) {
@@ -270,7 +325,7 @@ public class ConfigurationApi extends AttributeableEntity {
arrayBuilder.add(n);
}
objBuilder.add(InternalConstants.KEY_INTERNAL_FWK_PROPERTIES, arrayBuilder);
- }
+ }
return objBuilder;
}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
index 65922cd..2fff4a3 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/InternalConstants.java
@@ -68,4 +68,6 @@ abstract class InternalConstants {
static final String KEY_OPERATIONS = "operations";
static final String KEY_INTERNAL_NAMES = "internal-names";
+
+ static final String KEY_REGION = "region";
}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Region.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Region.java
new file mode 100644
index 0000000..d4c14f4
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/config/Region.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.feature.extension.apiregions.api.config;
+
+public enum Region {
+
+ GLOBAL,
+ INTERNAL;
+
+}
diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.builder.MergeHandler b/src/main/resources/META-INF/services/org.apache.sling.feature.builder.MergeHandler
index ec6db93..caa2534 100644
--- a/src/main/resources/META-INF/services/org.apache.sling.feature.builder.MergeHandler
+++ b/src/main/resources/META-INF/services/org.apache.sling.feature.builder.MergeHandler
@@ -1 +1,2 @@
org.apache.sling.feature.extension.apiregions.APIRegionMergeHandler
+org.apache.sling.feature.extension.apiregions.ConfigurationApiMergeHandler
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/ConfigurationApiMergeHandlerTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/ConfigurationApiMergeHandlerTest.java
new file mode 100644
index 0000000..5856910
--- /dev/null
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/ConfigurationApiMergeHandlerTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.Prototype;
+import org.apache.sling.feature.builder.BuilderContext;
+import org.apache.sling.feature.builder.FeatureBuilder;
+import org.apache.sling.feature.extension.apiregions.api.config.ConfigurationApi;
+import org.apache.sling.feature.extension.apiregions.api.config.Region;
+import org.junit.Test;
+
+public class ConfigurationApiMergeHandlerTest {
+
+ @Test public void testPrototypeRegionMerge() {
+ final Feature prototype = new Feature(ArtifactId.parse("g:p:1"));
+ final ConfigurationApi prototypeApi = new ConfigurationApi();
+ ConfigurationApi.setConfigurationApi(prototype, prototypeApi);
+
+ // always return prototype
+ final BuilderContext context = new BuilderContext(id -> prototype);
+ context.addMergeExtensions(new ConfigurationApiMergeHandler());
+
+ final Feature feature = new Feature(ArtifactId.parse("g:f:1"));
+ feature.setPrototype(new Prototype(prototype.getId()));
+ final ConfigurationApi featureApi = new ConfigurationApi();
+ ConfigurationApi.setConfigurationApi(feature, featureApi);
+
+ // no region
+ Feature result = FeatureBuilder.assemble(feature, context);
+ ConfigurationApi api = ConfigurationApi.getConfigurationApi(result);
+ assertNotNull(api);
+ assertNull(api.getRegion());
+
+ // prototype has region
+ prototypeApi.setRegion(Region.INTERNAL);
+ ConfigurationApi.setConfigurationApi(prototype, prototypeApi);
+ result = FeatureBuilder.assemble(feature, context);
+ api = ConfigurationApi.getConfigurationApi(result);
+ assertEquals(Region.INTERNAL, api.getRegion());
+
+ prototypeApi.setRegion(Region.GLOBAL);
+ ConfigurationApi.setConfigurationApi(prototype, prototypeApi);
+ result = FeatureBuilder.assemble(feature, context);
+ api = ConfigurationApi.getConfigurationApi(result);
+ assertEquals(Region.GLOBAL, api.getRegion());
+
+ // feature has region
+ prototypeApi.setRegion(null);
+ ConfigurationApi.setConfigurationApi(prototype, prototypeApi);
+ featureApi.setRegion(Region.INTERNAL);
+ ConfigurationApi.setConfigurationApi(feature, featureApi);
+ result = FeatureBuilder.assemble(feature, context);
+ api = ConfigurationApi.getConfigurationApi(result);
+ assertEquals(Region.INTERNAL, api.getRegion());
+
+ featureApi.setRegion(Region.GLOBAL);
+ ConfigurationApi.setConfigurationApi(feature, featureApi);
+ result = FeatureBuilder.assemble(feature, context);
+ api = ConfigurationApi.getConfigurationApi(result);
+ assertEquals(Region.GLOBAL, api.getRegion());
+
+ // both have region
+ prototypeApi.setRegion(Region.INTERNAL);
+ ConfigurationApi.setConfigurationApi(prototype, prototypeApi);
+ featureApi.setRegion(Region.INTERNAL);
+ ConfigurationApi.setConfigurationApi(feature, featureApi);
+ result = FeatureBuilder.assemble(feature, context);
+ api = ConfigurationApi.getConfigurationApi(result);
+ assertEquals(Region.INTERNAL, api.getRegion());
+
+ prototypeApi.setRegion(Region.GLOBAL);
+ ConfigurationApi.setConfigurationApi(prototype, prototypeApi);
+ featureApi.setRegion(Region.INTERNAL);
+ ConfigurationApi.setConfigurationApi(feature, featureApi);
+ result = FeatureBuilder.assemble(feature, context);
+ api = ConfigurationApi.getConfigurationApi(result);
+ assertEquals(Region.INTERNAL, api.getRegion());
+
+ prototypeApi.setRegion(Region.INTERNAL);
+ ConfigurationApi.setConfigurationApi(prototype, prototypeApi);
+ featureApi.setRegion(Region.GLOBAL);
+ ConfigurationApi.setConfigurationApi(feature, featureApi);
+ result = FeatureBuilder.assemble(feature, context);
+ api = ConfigurationApi.getConfigurationApi(result);
+ assertEquals(Region.GLOBAL, api.getRegion());
+
+ prototypeApi.setRegion(Region.GLOBAL);
+ ConfigurationApi.setConfigurationApi(prototype, prototypeApi);
+ featureApi.setRegion(Region.GLOBAL);
+ ConfigurationApi.setConfigurationApi(feature, featureApi);
+ result = FeatureBuilder.assemble(feature, context);
+ api = ConfigurationApi.getConfigurationApi(result);
+ assertEquals(Region.GLOBAL, api.getRegion());
+ }
+ }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
index f6beac6..a362501 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/ConfigurationApiTest.java
@@ -17,6 +17,7 @@
package org.apache.sling.feature.extension.apiregions.api.config;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -51,6 +52,20 @@ public class ConfigurationApiTest {
ConfigurationApi.getConfigurationApi(f);
}
+ @Test public void testSetConfigurationApi() {
+ final ConfigurationApi api = new ConfigurationApi();
+ final Feature f = new Feature(ArtifactId.parse("g:a:1"));
+
+ assertNull(f.getExtensions().getByName(ConfigurationApi.EXTENSION_NAME));
+
+ ConfigurationApi.setConfigurationApi(f, api);
+ assertNotNull(f.getExtensions().getByName(ConfigurationApi.EXTENSION_NAME));
+ assertNotNull(ConfigurationApi.getConfigurationApi(f));
+
+ ConfigurationApi.setConfigurationApi(f, null);
+ assertNull(f.getExtensions().getByName(ConfigurationApi.EXTENSION_NAME));
+ }
+
@Test public void testClear() {
final ConfigurationApi entity = new ConfigurationApi();
entity.getAttributes().put("a", Json.createValue(5));
@@ -60,6 +75,7 @@ public class ConfigurationApiTest {
entity.getInternalConfigurations().add("ipid");
entity.getInternalFactoryConfigurations().add("ifactory");
entity.getInternalFrameworkProperties().add("iprop");
+ entity.setRegion(Region.GLOBAL);
entity.clear();
assertTrue(entity.getAttributes().isEmpty());
assertTrue(entity.getConfigurationDescriptions().isEmpty());
@@ -68,6 +84,7 @@ public class ConfigurationApiTest {
assertTrue(entity.getInternalConfigurations().isEmpty());
assertTrue(entity.getInternalFactoryConfigurations().isEmpty());
assertTrue(entity.getInternalFrameworkProperties().isEmpty());
+ assertNull(entity.getRegion());
}
@Test public void testFromJSONObject() throws IOException {
@@ -77,7 +94,8 @@ public class ConfigurationApiTest {
"\"framework-properties\" : { \"prop\" : { \"type\" : \"STRING\"}}," +
"\"internal-configurations\" : [\"ipid\"],"+
"\"internal-factory-configurations\" : [\"ifactory\"],"+
- "\"internal-framework-properties\" : [\"iprop\"]}");
+ "\"internal-framework-properties\" : [\"iprop\"],"+
+ "\"region\" : \"INTERNAL\"}");
final ConfigurationApi entity = new ConfigurationApi();
entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
@@ -93,6 +111,7 @@ public class ConfigurationApiTest {
assertTrue(entity.getInternalConfigurations().contains("ipid"));
assertTrue(entity.getInternalFactoryConfigurations().contains("ifactory"));
assertTrue(entity.getInternalFrameworkProperties().contains("iprop"));
+ assertEquals(Region.INTERNAL, entity.getRegion());
}
@Test public void testToJSONObject() throws IOException {
@@ -104,6 +123,7 @@ public class ConfigurationApiTest {
entity.getInternalConfigurations().add("ipid");
entity.getInternalFactoryConfigurations().add("ifactory");
entity.getInternalFrameworkProperties().add("iprop");
+ entity.setRegion(Region.INTERNAL);
final Extension ext = new Extension(ExtensionType.JSON, "a", ExtensionState.OPTIONAL);
ext.setJSON("{ \"a\" : 5, \"configurations\" : { \"pid\": {}}, " +
@@ -111,8 +131,9 @@ public class ConfigurationApiTest {
"\"framework-properties\" : { \"prop\" : {}}," +
"\"internal-configurations\" : [\"ipid\"],"+
"\"internal-factory-configurations\" : [\"ifactory\"],"+
- "\"internal-framework-properties\" : [\"iprop\"]}");
+ "\"internal-framework-properties\" : [\"iprop\"],"+
+ "\"region\" : \"INTERNAL\"}");
- assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
+ assertEquals(ext.getJSONStructure().asJsonObject(), entity.toJSONObject());
}
}
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
similarity index 99%
rename from src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyTest.java
rename to src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
index 515d68d..d9b50c2 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/config/PropertyDescriptionTest.java
@@ -33,7 +33,7 @@ import org.apache.sling.feature.ExtensionState;
import org.apache.sling.feature.ExtensionType;
import org.junit.Test;
-public class PropertyTest {
+public class PropertyDescriptionTest {
@Test public void testClear() {
final PropertyDescription entity = new PropertyDescription();