You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2019/12/06 19:15:51 UTC

[sling-org-apache-sling-installer-core] branch master updated: SLING-8877 automatically register URL handlers for all UpdateHandler (#4)

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 762a38e  SLING-8877 automatically register URL handlers for all UpdateHandler (#4)
762a38e is described below

commit 762a38ec18f39cc16034a15e539086ef0c5ae36a
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Fri Dec 6 20:15:45 2019 +0100

    SLING-8877 automatically register URL handlers for all UpdateHandler (#4)
    
    * SLING-8877 automatically register URL handler for all UpdateHandler
    service schemes
---
 .../core/impl/InstallerResourceUrlHandler.java     |  86 ++++++++++++++
 .../installer/core/impl/OsgiInstallerImpl.java     |   5 +-
 .../installer/core/impl/UpdateHandlerTracker.java  | 132 +++++++++++++++++++++
 .../core/impl/tasks/AbstractBundleTask.java        |  12 ++
 .../core/impl/tasks/BundleInstallTask.java         |   1 +
 .../core/impl/tasks/BundleUpdateTask.java          |   1 +
 6 files changed, 235 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/apache/sling/installer/core/impl/InstallerResourceUrlHandler.java b/src/main/java/org/apache/sling/installer/core/impl/InstallerResourceUrlHandler.java
new file mode 100644
index 0000000..13f1974
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/core/impl/InstallerResourceUrlHandler.java
@@ -0,0 +1,86 @@
+/*
+ * 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.installer.core.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.ProtectionDomain;
+
+import org.apache.sling.installer.api.info.InfoProvider;
+import org.apache.sling.installer.api.info.ResourceGroup;
+import org.apache.sling.installer.api.tasks.ResourceState;
+import org.apache.sling.installer.core.impl.tasks.AbstractBundleTask;
+import org.osgi.service.url.AbstractURLStreamHandlerService;
+import org.osgi.service.url.URLStreamHandlerService;
+
+/**
+ * URL Handler for schemes used by the UpdateHandlers<br/>
+ * 
+ * This is for example used by Apache Felix in the context of populating the {@link ProtectionDomain} of a bundle's class
+ * by evaluating the bundle location.
+ * 
+ * @see <a href="https://osgi.org/specification/osgi.core/7.0.0/service.url.html#d0e42987">OSGi URL Handlers</a>
+ */
+public class InstallerResourceUrlHandler extends AbstractURLStreamHandlerService implements URLStreamHandlerService {
+
+    private final InfoProvider installerInfo;
+
+    public InstallerResourceUrlHandler(InfoProvider installerInfo) {
+        this.installerInfo = installerInfo;
+    }
+
+    private InputStream getInputStreamFromInstallerResourceUrl(URL url) throws IOException {
+        for (ResourceGroup resourceGroup : installerInfo.getInstallationState().getInstalledResources()) {
+            for (org.apache.sling.installer.api.info.Resource resource : resourceGroup.getResources()) {
+                String bundleLocation = AbstractBundleTask.getBundleLocation(resource);
+                if (url.toString().equals(bundleLocation) && resource.getState().equals(ResourceState.INSTALLED)) {
+                    return resource.getInputStream();
+                }
+            }
+        }
+        throw new IOException("Could not find OSGi installer resource with location " + url);
+    }
+
+    @Override
+    public URLConnection openConnection(URL url) throws IOException {
+        return new InputStreamConnection(url, getInputStreamFromInstallerResourceUrl(url));
+    }
+
+    private static final class InputStreamConnection extends URLConnection {
+
+        private final InputStream input;
+        
+        protected InputStreamConnection(URL url, InputStream input) {
+            super(url);
+            this.input = input;
+        }
+
+        @Override
+        public void connect() throws IOException {
+        }
+
+        @Override
+        public InputStream getInputStream() throws IOException {
+            return input;
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java b/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java
index b60685c..15ae3ed 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/OsgiInstallerImpl.java
@@ -70,6 +70,7 @@ import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.Version;
 import org.osgi.service.startlevel.StartLevel;
+import org.osgi.service.url.URLStreamHandlerService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -138,7 +139,7 @@ implements OsgiInstaller, ResourceChangeListener, RetryHandler, InfoProvider, Ru
     private SortingServiceTracker<ResourceTransformer> transformerTracker;
 
     /** A tracker for update handlers. */
-    private SortingServiceTracker<UpdateHandler> updateHandlerTracker;
+    private UpdateHandlerTracker updateHandlerTracker;
 
     /** A tracker for the factories. */
     private SortingServiceTracker<ResourceUpdater> updaterTracker;
@@ -236,7 +237,7 @@ implements OsgiInstaller, ResourceChangeListener, RetryHandler, InfoProvider, Ru
         // start service trackers
         this.factoryTracker = new SortingServiceTracker<>(ctx, InstallTaskFactory.class.getName(), this);
         this.transformerTracker = new SortingServiceTracker<>(ctx, ResourceTransformer.class.getName(), this);
-        this.updateHandlerTracker = new SortingServiceTracker<>(ctx, UpdateHandler.class.getName(), null);
+        this.updateHandlerTracker = new UpdateHandlerTracker(ctx, this);
         this.updaterTracker = new SortingServiceTracker<>(ctx, ResourceUpdater.class.getName(), this);
         this.factoryTracker.open();
         this.transformerTracker.open();
diff --git a/src/main/java/org/apache/sling/installer/core/impl/UpdateHandlerTracker.java b/src/main/java/org/apache/sling/installer/core/impl/UpdateHandlerTracker.java
new file mode 100644
index 0000000..f907486
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/core/impl/UpdateHandlerTracker.java
@@ -0,0 +1,132 @@
+/*
+ * 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.installer.core.impl;
+
+import java.security.ProtectionDomain;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.installer.api.UpdateHandler;
+import org.apache.sling.installer.api.info.InfoProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.url.URLConstants;
+import org.osgi.service.url.URLStreamHandlerService;
+
+/**
+ * Registers one (singleton) url handler handling all registered schemes of UpdateHandler's.
+ *
+ */
+public class UpdateHandlerTracker extends SortingServiceTracker<UpdateHandler> {
+
+    private final InfoProvider infoProvider;
+    private final Map<String, AtomicInteger> schemeUseCount;
+    private ServiceRegistration<URLStreamHandlerService> urlHandler;
+
+    public UpdateHandlerTracker(BundleContext ctx, InfoProvider infoProvider) {
+        super(ctx, UpdateHandler.class.getName(), null);
+        this.infoProvider = infoProvider;
+        this.urlHandler = null; // initialize lazily
+        this.schemeUseCount = new HashMap<>();
+    }
+
+    @Override
+    public Object addingService(ServiceReference reference) {
+        addOrRemoveService(reference, true);
+        return super.addingService(reference);
+    }
+
+    @Override
+    public void removedService(ServiceReference reference, Object service) {
+        addOrRemoveService(reference, false);
+        super.removedService(reference, service);
+    }
+
+    private void addOrRemoveService(ServiceReference reference, boolean isAdd) {
+        final String[] schemes = PropertiesUtil.toStringArray(reference.getProperty(UpdateHandler.PROPERTY_SCHEMES));
+        if (schemes != null && schemes.length > 0) {
+            boolean hasChanged = false;
+            for (String scheme : schemes) {
+                if (isAdd) {
+                    if (addScheme(scheme)) {
+                        hasChanged = true;
+                    }
+                } else {
+                    if (removeScheme(scheme)) {
+                        hasChanged = true;
+                    }
+                }
+            }
+            if (hasChanged) {
+                updateUrlStreamHandler();
+            }
+        }
+    }
+
+    private synchronized void updateUrlStreamHandler() {
+        if (schemeUseCount.isEmpty()) {
+            if (urlHandler != null) {
+                urlHandler.unregister();
+            }
+        } else {
+            if (urlHandler == null) {
+                InstallerResourceUrlHandler service = new InstallerResourceUrlHandler(infoProvider);
+                Dictionary<String, String[]> properties = new Hashtable<>();
+                properties.put(URLConstants.URL_HANDLER_PROTOCOL, schemeUseCount.keySet().toArray(new String[0]));
+                urlHandler = context.registerService(URLStreamHandlerService.class, service, properties);
+            } else {
+                Dictionary<String, String[]> properties = new Hashtable<>();
+                properties.put(URLConstants.URL_HANDLER_PROTOCOL, schemeUseCount.keySet().toArray(new String[0]));
+                urlHandler.setProperties(properties);
+            }
+        }
+    }
+
+    private synchronized boolean addScheme(String scheme) {
+        AtomicInteger useCount = schemeUseCount.get(scheme);
+        if (useCount == null) {
+            schemeUseCount.put(scheme, new AtomicInteger(1));
+            return true;
+        } else {
+            useCount.incrementAndGet();
+            return false;
+        }
+    }
+
+    private synchronized boolean removeScheme(String scheme) {
+        AtomicInteger useCount = schemeUseCount.get(scheme);
+        if (useCount != null && useCount.decrementAndGet() <= 0) {
+            schemeUseCount.remove(scheme);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void close() {
+        schemeUseCount.clear();
+        updateUrlStreamHandler();
+        super.close();
+    }
+}
diff --git a/src/main/java/org/apache/sling/installer/core/impl/tasks/AbstractBundleTask.java b/src/main/java/org/apache/sling/installer/core/impl/tasks/AbstractBundleTask.java
index 6c935f9..b5587c1 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/tasks/AbstractBundleTask.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/tasks/AbstractBundleTask.java
@@ -19,6 +19,8 @@
 package org.apache.sling.installer.core.impl.tasks;
 
 import org.apache.sling.installer.api.InstallableResource;
+import org.apache.sling.installer.api.info.Resource;
+import org.apache.sling.installer.api.tasks.TaskResource;
 import org.apache.sling.installer.api.tasks.TaskResourceGroup;
 import org.apache.sling.installer.core.impl.AbstractInstallTask;
 
@@ -27,10 +29,20 @@ import org.apache.sling.installer.core.impl.AbstractInstallTask;
  */
 public abstract class AbstractBundleTask extends AbstractInstallTask {
 
+    public final static String ATTRIBUTE_BUNDLE_LOCATION = "Bundle-Location";
+    
     public AbstractBundleTask(final TaskResourceGroup erl, final TaskSupport support) {
         super(erl, support);
     }
 
+    public static void setBundleLocation(TaskResource resource, String location) {
+        resource.setAttribute(ATTRIBUTE_BUNDLE_LOCATION, location);
+    }
+
+    public static String getBundleLocation(Resource resource) {
+        return (String) resource.getAttribute(ATTRIBUTE_BUNDLE_LOCATION);
+    }
+
     /**
      * Detect the start level for the resource.
      */
diff --git a/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleInstallTask.java b/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleInstallTask.java
index 7127e0a..6420eef 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleInstallTask.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleInstallTask.java
@@ -46,6 +46,7 @@ public class BundleInstallTask extends AbstractBundleTask {
         try {
             final Bundle b = this.getBundleContext().installBundle(getResource().getURL(), getResource().getInputStream());
             ctx.log("Installed bundle {} from resource {}", b, getResource());
+            setBundleLocation(getResource(), getResource().getURL());
             // optionally set the start level
             if ( startLevel > 0 ) {
                 final BundleStartLevel startLevelService = b.adapt(BundleStartLevel.class);
diff --git a/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java b/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java
index 8960ffc..4cd94bd 100644
--- a/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java
+++ b/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java
@@ -102,6 +102,7 @@ public class BundleUpdateTask extends AbstractBundleTask {
             b.update(getResource().getInputStream());
             ctx.log("Updated bundle {} from resource {}", b, getResource());
 
+            setBundleLocation(getResource(), b.getLocation());
             // start level handling - after update to avoid starting the bundle
             // just before the update
             final BundleStartLevel startLevelService = b.adapt(BundleStartLevel.class);