Support installation by reference for equinox
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 29 Oct 2022 06:33:51 +0000 (08:33 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 29 Oct 2022 06:33:51 +0000 (08:33 +0200)
org.argeo.init/src/org/argeo/init/a2/A2Source.java
org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java
org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java
org.argeo.init/src/org/argeo/init/a2/FsA2Source.java
org.argeo.init/src/org/argeo/init/a2/FsM2Source.java
org.argeo.init/src/org/argeo/init/a2/OsgiContext.java
org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java
org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java

index c28df3b238bf4a7acc85e4198fc93d55271e9356..5c8329c85e8616329a2154047273ac4b4ed943dd 100644 (file)
@@ -4,8 +4,15 @@ import java.net.URI;
 
 /** A provisioning source in A2 format. */
 public interface A2Source extends ProvisioningSource {
+       /** Use standard a2 protocol, installing from source URL. */
        final static String SCHEME_A2 = "a2";
+       /**
+        * Use equinox-specific reference: installation, which does not copy the bundle
+        * content.
+        */
+       final static String SCHEME_A2_REFERENCE = "a2+reference";
        final static String DEFAULT_A2_URI = SCHEME_A2 + ":///";
+       final static String DEFAULT_A2_REFERENCE_URI = SCHEME_A2_REFERENCE + ":///";
 
        URI getUri();
 }
index 0c3cefd014a915f7a02d0c5b8caf69c858a0b6e1..7df851b2452c0d788c5618a9372238459d712021 100644 (file)
@@ -28,6 +28,12 @@ import org.osgi.framework.Version;
 public abstract class AbstractProvisioningSource implements ProvisioningSource {
        protected final Map<String, A2Contribution> contributions = Collections.synchronizedSortedMap(new TreeMap<>());
 
+       private final boolean useReference;
+
+       public AbstractProvisioningSource(boolean useReference) {
+               this.useReference = useReference;
+       }
+
        public Iterable<A2Contribution> listContributions(Object filter) {
                return contributions.values();
        }
@@ -35,16 +41,25 @@ public abstract class AbstractProvisioningSource implements ProvisioningSource {
        @Override
        public Bundle install(BundleContext bc, A2Module module) {
                try {
-                       Path tempJar = null;
-                       if (module.getLocator() instanceof Path && Files.isDirectory((Path) module.getLocator()))
-                               tempJar = toTempJar((Path) module.getLocator());
-                       Bundle bundle;
-                       try (InputStream in = newInputStream(tempJar != null ? tempJar : module.getLocator())) {
-                               bundle = bc.installBundle(module.getBranch().getCoordinates(), in);
+                       Object locator = module.getLocator();
+                       if (useReference && locator instanceof Path locatorPath) {
+                               String referenceUrl = "reference:file:" + locatorPath.toString();
+                               Bundle bundle = bc.installBundle(referenceUrl);
+                               return bundle;
+                       } else {
+
+                               Path tempJar = null;
+                               if (locator instanceof Path && Files.isDirectory((Path) locator))
+                                       tempJar = toTempJar((Path) locator);
+                               Bundle bundle;
+                               try (InputStream in = newInputStream(tempJar != null ? tempJar : locator)) {
+                                       bundle = bc.installBundle(module.getBranch().getCoordinates(), in);
+                               }
+
+                               if (tempJar != null)
+                                       Files.deleteIfExists(tempJar);
+                               return bundle;
                        }
-                       if (tempJar != null)
-                               Files.deleteIfExists(tempJar);
-                       return bundle;
                } catch (BundleException | IOException e) {
                        throw new A2Exception("Cannot install module " + module, e);
                }
@@ -53,14 +68,21 @@ public abstract class AbstractProvisioningSource implements ProvisioningSource {
        @Override
        public void update(Bundle bundle, A2Module module) {
                try {
-                       Path tempJar = null;
-                       if (module.getLocator() instanceof Path && Files.isDirectory((Path) module.getLocator()))
-                               tempJar = toTempJar((Path) module.getLocator());
-                       try (InputStream in = newInputStream(tempJar != null ? tempJar : module.getLocator())) {
-                               bundle.update(in);
+                       Object locator = module.getLocator();
+                       if (useReference && locator instanceof Path) {
+                               try (InputStream in = newInputStream(locator)) {
+                                       bundle.update(in);
+                               }
+                       } else {
+                               Path tempJar = null;
+                               if (locator instanceof Path && Files.isDirectory((Path) locator))
+                                       tempJar = toTempJar((Path) locator);
+                               try (InputStream in = newInputStream(tempJar != null ? tempJar : locator)) {
+                                       bundle.update(in);
+                               }
+                               if (tempJar != null)
+                                       Files.deleteIfExists(tempJar);
                        }
-                       if (tempJar != null)
-                               Files.deleteIfExists(tempJar);
                } catch (BundleException | IOException e) {
                        throw new A2Exception("Cannot update module " + module, e);
                }
index 8a9e5e67f1f323bb2755a16e5f11a0726c64fc1b..12de4228ba9a57e55eb3c43a180bb1810db349e4 100644 (file)
@@ -11,10 +11,15 @@ import org.argeo.init.osgi.OsgiBootUtils;
 import org.osgi.framework.Version;
 
 /**
- * A provisioning source based on the linear classpath with which the JCM has
+ * A provisioning source based on the linear classpath with which the JVM has
  * been started.
  */
 public class ClasspathSource extends AbstractProvisioningSource {
+       
+       public ClasspathSource() {
+               super(true);
+       }
+
        void load() throws IOException {
                A2Contribution classpathContribution = getOrAddContribution( A2Contribution.CLASSPATH);
                List<String> classpath = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator));
index 5099eed15be9509ec516a44223dc095b07114025..d4aa863869aa682f3aec5c92e80e8212d8042f2c 100644 (file)
@@ -6,7 +6,6 @@ import java.net.URISyntaxException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.SortedMap;
@@ -18,15 +17,16 @@ import org.osgi.framework.Version;
 /** A file system {@link AbstractProvisioningSource} in A2 format. */
 public class FsA2Source extends AbstractProvisioningSource implements A2Source {
        private final Path base;
-       private Map<String, String> xOr;
+       private final Map<String, String> xOr;
 
-       public FsA2Source(Path base) {
-               this(base, new HashMap<>());
-       }
+//     public FsA2Source(Path base) {
+//             this(base, new HashMap<>());
+//     }
 
-       public FsA2Source(Path base, Map<String, String> xOr) {
+       public FsA2Source(Path base, Map<String, String> xOr, boolean useReference) {
+               super(useReference);
                this.base = base;
-               this.xOr = xOr;
+               this.xOr = new HashMap<>(xOr);
        }
 
        void load() throws IOException {
@@ -92,19 +92,8 @@ public class FsA2Source extends AbstractProvisioningSource implements A2Source {
                                        String ext = moduleFileName.substring(lastDot + 1);
                                        if (!"jar".equals(ext))
                                                continue modules;
-//                                     String moduleName = moduleFileName.substring(0, lastDot);
-//                                     if (moduleName.endsWith("-SNAPSHOT"))
-//                                             moduleName = moduleName.substring(0, moduleName.length() - "-SNAPSHOT".length());
-//                                     int lastDash = moduleName.lastIndexOf('-');
-//                                     String versionStr = moduleName.substring(lastDash + 1);
-//                                     String componentName = moduleName.substring(0, lastDash);
-                                       // if(versionStr.endsWith("-SNAPSHOT")) {
-                                       // versionStr = readVersionFromModule(modulePath);
-                                       // }
                                        Version version;
-//                                     try {
-//                                             version = new Version(versionStr);
-//                                     } catch (Exception e) {
+                                       // TODO optimise? check attributes?
                                        String[] nameVersion = readNameVersionFromModule(modulePath);
                                        String componentName = nameVersion[0];
                                        String versionStr = nameVersion[1];
@@ -139,19 +128,19 @@ public class FsA2Source extends AbstractProvisioningSource implements A2Source {
                }
        }
 
-       public static void main(String[] args) {
-               if (args.length == 0)
-                       throw new IllegalArgumentException("Usage: <path to A2 base>");
-               try {
-                       Map<String, String> xOr = new HashMap<>();
-                       xOr.put("osgi", "equinox");
-                       xOr.put("swt", "rap");
-                       FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
-                       context.load();
-                       context.asTree();
-               } catch (Exception e) {
-                       e.printStackTrace();
-               }
-       }
+//     public static void main(String[] args) {
+//             if (args.length == 0)
+//                     throw new IllegalArgumentException("Usage: <path to A2 base>");
+//             try {
+//                     Map<String, String> xOr = new HashMap<>();
+//                     xOr.put("osgi", "equinox");
+//                     xOr.put("swt", "rap");
+//                     FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
+//                     context.load();
+//                     context.asTree();
+//             } catch (Exception e) {
+//                     e.printStackTrace();
+//             }
+//     }
 
 }
index 1657fad57e9835784f9a39890ea9b8558e732821..0313d20f367ed32d2fa44a24f213f3decb76daa9 100644 (file)
@@ -17,7 +17,7 @@ public class FsM2Source extends AbstractProvisioningSource {
        private final Path base;
 
        public FsM2Source(Path base) {
-               super();
+               super(false);
                this.base = base;
        }
 
index 35fbee3568a4b3ea39d0709423ab4d33d31c1499..0064ab9eddbb1c4d3c8c37148eb9efaaa68f61aa 100644 (file)
@@ -11,11 +11,12 @@ class OsgiContext extends AbstractProvisioningSource {
        private final BundleContext bc;
 
        public OsgiContext(BundleContext bc) {
-               super();
+               super(false);
                this.bc = bc;
        }
 
        public OsgiContext() {
+               super(false);
                Bundle bundle = FrameworkUtil.getBundle(OsgiContext.class);
                if (bundle == null)
                        throw new IllegalArgumentException(
index 6d842650c2620fd2d26249bf69d7d741a37aa3fd..a237fdf48aecaa79349df7f0999c183c0e05a8e6 100644 (file)
@@ -1,5 +1,8 @@
 package org.argeo.init.a2;
 
+import static org.argeo.init.a2.A2Source.SCHEME_A2;
+import static org.argeo.init.a2.A2Source.SCHEME_A2_REFERENCE;
+
 import java.io.File;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
@@ -9,7 +12,6 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -25,7 +27,6 @@ import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.Version;
-import org.osgi.framework.launch.Framework;
 import org.osgi.framework.wiring.FrameworkWiring;
 
 /** Loads provisioning sources into an OSGi context. */
@@ -72,7 +73,7 @@ public class ProvisioningManager {
                                xOr.put(key, lst.get(0));
                        }
 
-                       if (A2Source.SCHEME_A2.equals(u.getScheme())) {
+                       if (SCHEME_A2.equals(u.getScheme()) || SCHEME_A2_REFERENCE.equals(u.getScheme())) {
                                if (u.getHost() == null || "".equals(u.getHost())) {
                                        String baseStr = u.getPath();
                                        if (File.separatorChar == '\\') {// MS Windows
@@ -80,12 +81,19 @@ public class ProvisioningManager {
                                        }
                                        Path base = Paths.get(baseStr);
                                        if (Files.exists(base)) {
-                                               FsA2Source source = new FsA2Source(base, xOr);
+                                               FsA2Source source = new FsA2Source(base, xOr, SCHEME_A2_REFERENCE.equals(u.getScheme()));
                                                source.load();
                                                addSource(source);
                                                OsgiBootUtils.info("Registered " + uri + " as source");
+                                       } else {
+                                               OsgiBootUtils.debug("Source " + base + " does not exist, ignoring.");
                                        }
+                               } else {
+                                       throw new UnsupportedOperationException(
+                                                       "Remote installation is not yet supported, cannot add source " + u);
                                }
+                       } else {
+                               throw new IllegalArgumentException("Unkown scheme: for source " + u);
                        }
                } catch (Exception e) {
                        throw new A2Exception("Cannot add source " + uri, e);
@@ -217,46 +225,46 @@ public class ProvisioningManager {
                }
        }
 
-       public static void main(String[] args) {
-               if (args.length == 0)
-                       throw new IllegalArgumentException("Usage: <path to A2 base>");
-               Map<String, String> configuration = new HashMap<>();
-               configuration.put("osgi.console", "2323");
-               configuration.put("org.osgi.framework.bootdelegation",
-                               "com.sun.jndi.ldap,com.sun.jndi.ldap.sasl,com.sun.security.jgss,com.sun.jndi.dns,com.sun.nio.file,com.sun.nio.sctp,sun.nio.cs");
-               Framework framework = OsgiBootUtils.launch(configuration);
-               try {
-                       ProvisioningManager pm = new ProvisioningManager(framework.getBundleContext());
-                       Map<String, String> xOr = new HashMap<>();
-                       xOr.put("osgi", "equinox");
-                       xOr.put("swt", "rap");
-                       FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
-                       context.load();
-                       pm.addSource(context);
-                       if (framework.getBundleContext().getBundles().length == 1) {// initial
-                               pm.install(null);
-                       } else {
-                               pm.update();
-                       }
-
-                       Thread.sleep(2000);
-
-                       Bundle[] bundles = framework.getBundleContext().getBundles();
-                       Arrays.sort(bundles, (b1, b2) -> b1.getSymbolicName().compareTo(b2.getSymbolicName()));
-                       for (Bundle b : bundles)
-                               if (b.getState() == Bundle.RESOLVED || b.getState() == Bundle.STARTING || b.getState() == Bundle.ACTIVE)
-                                       System.out.println(b.getSymbolicName() + " " + b.getVersion());
-                               else
-                                       System.err.println(b.getSymbolicName() + " " + b.getVersion() + " (" + b.getState() + ")");
-               } catch (Exception e) {
-                       e.printStackTrace();
-               } finally {
-                       try {
-                               framework.stop();
-                       } catch (Exception e) {
-                               e.printStackTrace();
-                       }
-               }
-       }
+//     public static void main(String[] args) {
+//             if (args.length == 0)
+//                     throw new IllegalArgumentException("Usage: <path to A2 base>");
+//             Map<String, String> configuration = new HashMap<>();
+//             configuration.put("osgi.console", "2323");
+//             configuration.put("org.osgi.framework.bootdelegation",
+//                             "com.sun.jndi.ldap,com.sun.jndi.ldap.sasl,com.sun.security.jgss,com.sun.jndi.dns,com.sun.nio.file,com.sun.nio.sctp,sun.nio.cs");
+//             Framework framework = OsgiBootUtils.launch(configuration);
+//             try {
+//                     ProvisioningManager pm = new ProvisioningManager(framework.getBundleContext());
+//                     Map<String, String> xOr = new HashMap<>();
+//                     xOr.put("osgi", "equinox");
+//                     xOr.put("swt", "rap");
+//                     FsA2Source context = new FsA2Source(Paths.get(args[0]), xOr);
+//                     context.load();
+//                     pm.addSource(context);
+//                     if (framework.getBundleContext().getBundles().length == 1) {// initial
+//                             pm.install(null);
+//                     } else {
+//                             pm.update();
+//                     }
+//
+//                     Thread.sleep(2000);
+//
+//                     Bundle[] bundles = framework.getBundleContext().getBundles();
+//                     Arrays.sort(bundles, (b1, b2) -> b1.getSymbolicName().compareTo(b2.getSymbolicName()));
+//                     for (Bundle b : bundles)
+//                             if (b.getState() == Bundle.RESOLVED || b.getState() == Bundle.STARTING || b.getState() == Bundle.ACTIVE)
+//                                     System.out.println(b.getSymbolicName() + " " + b.getVersion());
+//                             else
+//                                     System.err.println(b.getSymbolicName() + " " + b.getVersion() + " (" + b.getState() + ")");
+//             } catch (Exception e) {
+//                     e.printStackTrace();
+//             } finally {
+//                     try {
+//                             framework.stop();
+//                     } catch (Exception e) {
+//                             e.printStackTrace();
+//                     }
+//             }
+//     }
 
 }
index 8573615ed11a7a528f266e006e4f0457941fed5a..9f3bd25f783df4908aaa4c6bd8bfffa8c200597e 100644 (file)
@@ -104,6 +104,13 @@ public class OsgiBoot implements OsgiBootConstants {
                                                                A2Source.SCHEME_A2 + "://" + homePath.toString() + "/.local/share/a2" + queryPart);
                                        provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/share/a2" + queryPart);
                                        provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/share/a2" + queryPart);
+                               } else if (source.trim().equals(A2Source.DEFAULT_A2_REFERENCE_URI)) {
+                                       if (Files.exists(homePath))
+                                               provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + "://" + homePath.toString()
+                                                               + "/.local/share/a2" + queryPart);
+                                       provisioningManager
+                                                       .registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/local/share/a2" + queryPart);
+                                       provisioningManager.registerSource(A2Source.SCHEME_A2_REFERENCE + ":///usr/share/a2" + queryPart);
                                } else {
                                        provisioningManager.registerSource(source + queryPart);
                                }