This is an automated email from the ASF dual-hosted git repository.
radu pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-sightly.git
The following commit(s) were added to refs/heads/master by this push:
new e554fa4 SLING-8045 - The ExtensionRegistryService does not correctly
manage references
e554fa4 is described below
commit e554fa42c718fa0ee5ff516ad3a4251c70127ca7
Author: Radu Cotescu <[email protected]>
AuthorDate: Fri Oct 19 17:14:08 2018 +0200
SLING-8045 - The ExtensionRegistryService does not correctly manage
references
* work with ServiceReferences only in the unbind method
* implemented fallback when an extension is unregistered, so that a lower
priority extension becomes the new active extension for a certain operation
after a higher priority extension is unregistered
---
pom.xml | 5 +-
.../impl/engine/ExtensionRegistryService.java | 101 ++++++++++++---------
.../impl/engine/RuntimeExtensionReference.java | 77 ++++++++++++++++
.../impl/engine/ExtensionRegistryServiceTest.java | 83 +++++++++++++++++
4 files changed, 219 insertions(+), 47 deletions(-)
diff --git a/pom.xml b/pom.xml
index b4c88b1..025954f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,6 +56,7 @@
<properties>
<jacoco.maven.plugin.version>0.7.9</jacoco.maven.plugin.version>
+ <sling.java.version>8</sling.java.version>
</properties>
<!--
======================================================================= -->
@@ -313,8 +314,8 @@
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.testing.sling-mock</artifactId>
- <version>2.1.2</version>
+ <artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId>
+ <version>2.3.4</version>
<scope>test</scope>
</dependency>
</dependencies>
diff --git
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/ExtensionRegistryService.java
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/ExtensionRegistryService.java
index 73b9de0..4c7ecbc 100644
---
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/ExtensionRegistryService.java
+++
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/ExtensionRegistryService.java
@@ -1,30 +1,32 @@
-/*******************************************************************************
- * 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.
-
******************************************************************************/
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.impl.engine;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.scripting.sightly.extension.RuntimeExtension;
-import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
@@ -37,7 +39,7 @@ import org.osgi.service.component.annotations.ReferencePolicy;
public class ExtensionRegistryService {
private volatile Map<String, RuntimeExtension> mapping = new HashMap<>();
- private Map<String, Integer> mappingPriorities = new HashMap<>(10, 0.9f);
+ private final Map<String, TreeSet<RuntimeExtensionReference>> extensions =
new HashMap<>();
public Map<String, RuntimeExtension> extensions() {
return mapping;
@@ -49,38 +51,47 @@ public class ExtensionRegistryService {
cardinality = ReferenceCardinality.MULTIPLE
)
@SuppressWarnings("unused")
- protected synchronized void bindExtensionService(RuntimeExtension
extension, Map<String, Object> properties) {
- Integer newPriority =
PropertiesUtil.toInteger(properties.get(Constants.SERVICE_RANKING), 0);
- String extensionName =
PropertiesUtil.toString(properties.get(RuntimeExtension.NAME), "");
- Integer priority =
PropertiesUtil.toInteger(mappingPriorities.get(extensionName), 0);
- if (newPriority > priority) {
- mapping = Collections.unmodifiableMap(add(mapping, extension,
extensionName));
- mappingPriorities.put(extensionName, newPriority);
- } else {
- if (!mapping.containsKey(extensionName)) {
- mapping = Collections.unmodifiableMap(add(mapping, extension,
extensionName));
- mappingPriorities.put(extensionName, newPriority);
+ protected void bindExtensionService(ServiceReference<RuntimeExtension>
serviceReference, RuntimeExtension runtimeExtension) {
+ RuntimeExtensionReference rer = new
RuntimeExtensionReference(serviceReference, runtimeExtension);
+ synchronized (extensions) {
+ Set<RuntimeExtensionReference> namedExtensions =
extensions.computeIfAbsent(rer.getName(), key -> new TreeSet<>());
+ if (namedExtensions.add(rer)) {
+ mapping = getRuntimeExtensions();
}
}
-
}
@SuppressWarnings("unused")
- protected synchronized void unbindExtensionService(RuntimeExtension
extension, Map<String, Object> properties) {
- String extensionName =
PropertiesUtil.toString(properties.get(RuntimeExtension.NAME), "");
- mappingPriorities.remove(extensionName);
- mapping = Collections.unmodifiableMap(remove(mapping, extensionName));
+ protected void unbindExtensionService(ServiceReference<RuntimeExtension>
serviceReference) {
+ synchronized (extensions) {
+ String name =
PropertiesUtil.toString(serviceReference.getProperty(RuntimeExtension.NAME),
"");
+ Set<RuntimeExtensionReference> namedExtensions =
extensions.get(name);
+ boolean changed = false;
+ if (namedExtensions != null) {
+ for (RuntimeExtensionReference runtimeExtensionReference :
namedExtensions) {
+ if
(serviceReference.equals(runtimeExtensionReference.getServiceReference())) {
+ namedExtensions.remove(runtimeExtensionReference);
+ if (namedExtensions.isEmpty()) {
+ extensions.remove(name);
+ }
+ changed = true;
+ break;
+ }
+ }
+ }
+ if (changed) {
+ mapping = getRuntimeExtensions();
+ }
+ }
}
- private Map<String, RuntimeExtension> add(Map<String, RuntimeExtension>
oldMap, RuntimeExtension extension, String extensionName) {
- HashMap<String, RuntimeExtension> newMap = new HashMap<>(oldMap);
- newMap.put(extensionName, extension);
- return newMap;
+ private Map<String, RuntimeExtension> getRuntimeExtensions() {
+ HashMap<String, RuntimeExtension> replacement = new HashMap<>();
+ for (Map.Entry<String, TreeSet<RuntimeExtensionReference>> entry :
extensions.entrySet()) {
+ replacement.put(entry.getKey(),
entry.getValue().last().getService());
+ }
+ return Collections.unmodifiableMap(replacement);
}
- private Map<String, RuntimeExtension> remove(Map<String, RuntimeExtension>
oldMap, String extensionName) {
- HashMap<String, RuntimeExtension> newMap = new HashMap<>(oldMap);
- newMap.remove(extensionName);
- return newMap;
- }
+
}
diff --git
a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/RuntimeExtensionReference.java
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/RuntimeExtensionReference.java
new file mode 100644
index 0000000..96904b6
--- /dev/null
+++
b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/RuntimeExtensionReference.java
@@ -0,0 +1,77 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.impl.engine;
+
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.scripting.sightly.extension.RuntimeExtension;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+class RuntimeExtensionReference implements
Comparable<RuntimeExtensionReference> {
+ private final int priority;
+ private final String name;
+ private ServiceReference<RuntimeExtension> serviceReference;
+ private RuntimeExtension runtimeExtension;
+
+ RuntimeExtensionReference(ServiceReference<RuntimeExtension>
serviceReference, RuntimeExtension runtimeExtension) {
+ this.serviceReference = serviceReference;
+ this.runtimeExtension = runtimeExtension;
+ priority =
PropertiesUtil.toInteger(serviceReference.getProperty(Constants.SERVICE_RANKING),
0);
+ name =
PropertiesUtil.toString(serviceReference.getProperty(RuntimeExtension.NAME),
"");
+ }
+
+ @Override
+ public int compareTo(@NotNull RuntimeExtensionReference other) {
+ if (equals(other)) {
+ return 0;
+ }
+ if (name.equals(other.name)) {
+ return priority - other.priority;
+ }
+ return -1;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof RuntimeExtensionReference) {
+ RuntimeExtensionReference other = (RuntimeExtensionReference) obj;
+ return serviceReference.equals(other.serviceReference);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return serviceReference.hashCode();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ ServiceReference<RuntimeExtension> getServiceReference() {
+ return serviceReference;
+ }
+
+ synchronized RuntimeExtension getService() {
+ return runtimeExtension;
+ }
+
+}
diff --git
a/src/test/java/org/apache/sling/scripting/sightly/impl/engine/ExtensionRegistryServiceTest.java
b/src/test/java/org/apache/sling/scripting/sightly/impl/engine/ExtensionRegistryServiceTest.java
new file mode 100644
index 0000000..32ecf4c
--- /dev/null
+++
b/src/test/java/org/apache/sling/scripting/sightly/impl/engine/ExtensionRegistryServiceTest.java
@@ -0,0 +1,83 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scripting.sightly.impl.engine;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.sling.scripting.sightly.extension.RuntimeExtension;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+public class ExtensionRegistryServiceTest {
+
+ @Rule
+ public final SlingContext slingContext = new SlingContext();
+
+ @Before
+ public void before() {
+ slingContext.registerInjectActivateService(new
ExtensionRegistryService());
+ }
+
+ @Test
+ public void testRegistryForExtensionRanking() throws Exception {
+ RuntimeExtension extension1 = mock(RuntimeExtension.class);
+ ServiceRegistration<RuntimeExtension> registration1 =
slingContext.bundleContext().registerService(
+ RuntimeExtension.class,
+ extension1,
+ new Hashtable<String, Object>() {{
+ put(RuntimeExtension.NAME, "test");
+ }}
+ );
+
+
+ RuntimeExtension extension2 = mock(RuntimeExtension.class);
+ ServiceRegistration<RuntimeExtension> registration2 =
slingContext.bundleContext().registerService(
+ RuntimeExtension.class,
+ extension2,
+ new Hashtable<String, Object>() {{
+ put(Constants.SERVICE_RANKING, 2);
+ put(RuntimeExtension.NAME, "test");
+ }}
+ );
+
+ ExtensionRegistryService registryService =
slingContext.getService(ExtensionRegistryService.class);
+ assertNotNull("The ExtensionRegistryService should have been
registered.", registryService);
+
+ assertEquals(1, registryService.extensions().size());
+ assertEquals(extension2, registryService.extensions().get("test"));
+
+ registration2.unregister();
+ assertEquals(1, registryService.extensions().size());
+ assertEquals(extension1, registryService.extensions().get("test"));
+
+ registration1.unregister();
+ assertEquals(0, registryService.extensions().size());
+ }
+
+
+}