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());
+    }
+
+
+}

Reply via email to