From c66685995c1bf2c59bdf6d68753470c85828310a Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sat, 29 Oct 2022 08:33:51 +0200 Subject: [PATCH] Support installation by reference for equinox --- .../src/org/argeo/init/a2/A2Source.java | 7 ++ .../init/a2/AbstractProvisioningSource.java | 54 +++++++--- .../org/argeo/init/a2/ClasspathSource.java | 7 +- .../src/org/argeo/init/a2/FsA2Source.java | 55 +++++------ .../src/org/argeo/init/a2/FsM2Source.java | 2 +- .../src/org/argeo/init/a2/OsgiContext.java | 3 +- .../argeo/init/a2/ProvisioningManager.java | 98 ++++++++++--------- .../src/org/argeo/init/osgi/OsgiBoot.java | 7 ++ 8 files changed, 136 insertions(+), 97 deletions(-) diff --git a/org.argeo.init/src/org/argeo/init/a2/A2Source.java b/org.argeo.init/src/org/argeo/init/a2/A2Source.java index c28df3b23..5c8329c85 100644 --- a/org.argeo.init/src/org/argeo/init/a2/A2Source.java +++ b/org.argeo.init/src/org/argeo/init/a2/A2Source.java @@ -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(); } diff --git a/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java b/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java index 0c3cefd01..7df851b24 100644 --- a/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java +++ b/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java @@ -28,6 +28,12 @@ import org.osgi.framework.Version; public abstract class AbstractProvisioningSource implements ProvisioningSource { protected final Map contributions = Collections.synchronizedSortedMap(new TreeMap<>()); + private final boolean useReference; + + public AbstractProvisioningSource(boolean useReference) { + this.useReference = useReference; + } + public Iterable 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); } diff --git a/org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java b/org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java index 8a9e5e67f..12de4228b 100644 --- a/org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java +++ b/org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java @@ -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 classpath = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator)); diff --git a/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java b/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java index 5099eed15..d4aa86386 100644 --- a/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java +++ b/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java @@ -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 xOr; + private final Map xOr; - public FsA2Source(Path base) { - this(base, new HashMap<>()); - } +// public FsA2Source(Path base) { +// this(base, new HashMap<>()); +// } - public FsA2Source(Path base, Map xOr) { + public FsA2Source(Path base, Map 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: "); - try { - Map 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: "); +// try { +// Map 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(); +// } +// } } diff --git a/org.argeo.init/src/org/argeo/init/a2/FsM2Source.java b/org.argeo.init/src/org/argeo/init/a2/FsM2Source.java index 1657fad57..0313d20f3 100644 --- a/org.argeo.init/src/org/argeo/init/a2/FsM2Source.java +++ b/org.argeo.init/src/org/argeo/init/a2/FsM2Source.java @@ -17,7 +17,7 @@ public class FsM2Source extends AbstractProvisioningSource { private final Path base; public FsM2Source(Path base) { - super(); + super(false); this.base = base; } diff --git a/org.argeo.init/src/org/argeo/init/a2/OsgiContext.java b/org.argeo.init/src/org/argeo/init/a2/OsgiContext.java index 35fbee356..0064ab9ed 100644 --- a/org.argeo.init/src/org/argeo/init/a2/OsgiContext.java +++ b/org.argeo.init/src/org/argeo/init/a2/OsgiContext.java @@ -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( diff --git a/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java b/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java index 6d842650c..a237fdf48 100644 --- a/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java +++ b/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java @@ -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: "); - Map 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 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: "); +// Map 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 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(); +// } +// } +// } } diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java index 8573615ed..9f3bd25f7 100644 --- a/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java @@ -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); } -- 2.30.2