From 4f53d33d6a65a123ab8f69b8580de1da2e029a0a Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 14 Jul 2022 18:11:23 +0200 Subject: [PATCH] A2 variants supported --- Makefile | 15 +++- Makefile-rcp.mk | 10 ++- .../src/org/argeo/init/a2/A2Contribution.java | 2 + .../init/a2/AbstractProvisioningSource.java | 19 +++++ .../src/org/argeo/init/a2/FsA2Source.java | 75 ++++++++++++++++--- .../argeo/init/a2/ProvisioningManager.java | 70 ++++++++++++++++- .../src/org/argeo/init/osgi/OsgiBoot.java | 13 +++- 7 files changed, 179 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index 7129324be..125205d57 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,16 @@ include sdk.mk all: osgi jni move-rap $(MAKE) -f Makefile-rcp.mk -move-rap: osgi - mkdir -p $(A2_OUTPUT)/$(A2_CATEGORY).eclipse.rap - mv -v $(A2_OUTPUT)/$(A2_CATEGORY)/*.rap.$(MAJOR).$(MINOR).jar $(A2_OUTPUT)/$(A2_CATEGORY).eclipse.rap +move-swt: osgi + mkdir -p $(A2_OUTPUT)/swt/$(A2_CATEGORY) + mv -v $(A2_OUTPUT)/$(A2_CATEGORY)/org.argeo.swt.minidesktop.$(MAJOR).$(MINOR).jar $(A2_OUTPUT)/swt/$(A2_CATEGORY) + mv -v $(A2_OUTPUT)/$(A2_CATEGORY)/org.argeo.cms.swt.$(MAJOR).$(MINOR).jar $(A2_OUTPUT)/swt/$(A2_CATEGORY) + mv -v $(A2_OUTPUT)/$(A2_CATEGORY)/org.argeo.cms.e4.$(MAJOR).$(MINOR).jar $(A2_OUTPUT)/swt/$(A2_CATEGORY) + +move-rap: move-swt + mkdir -p $(A2_OUTPUT)/swt/rap/$(A2_CATEGORY) + mv -v $(A2_OUTPUT)/$(A2_CATEGORY)/*.rap.$(MAJOR).$(MINOR).jar $(A2_OUTPUT)/swt/rap/$(A2_CATEGORY) + mv -v $(A2_OUTPUT)/$(A2_CATEGORY)/*.rap.cli.$(MAJOR).$(MINOR).jar $(A2_OUTPUT)/swt/rap/$(A2_CATEGORY) touch $(BUILD_BASE)/*.rap/bnd.bnd A2_CATEGORY = org.argeo.cms @@ -55,8 +62,8 @@ DEP_CATEGORIES = \ org.argeo.tp \ org.argeo.tp.apache \ org.argeo.tp.jetty \ -org.argeo.tp.eclipse \ osgi/api/org.argeo.tp.osgi \ +osgi/equinox/org.argeo.tp.eclipse \ swt/rap/org.argeo.tp.swt \ swt/rap/org.argeo.tp.swt.workbench \ org.argeo.tp.jcr diff --git a/Makefile-rcp.mk b/Makefile-rcp.mk index 6dfa39824..f09721089 100644 --- a/Makefile-rcp.mk +++ b/Makefile-rcp.mk @@ -1,9 +1,15 @@ include sdk.mk .PHONY: clean all osgi -all: osgi +all: osgi move-rcp -A2_CATEGORY = org.argeo.cms.eclipse.rcp +move-rcp: osgi + mkdir -p $(A2_OUTPUT)/swt/rcp/$(A2_CATEGORY) + mv -v $(A2_OUTPUT)/$(A2_CATEGORY)/*.rcp.$(MAJOR).$(MINOR).jar $(A2_OUTPUT)/swt/rcp/$(A2_CATEGORY) + mv -v $(A2_OUTPUT)/$(A2_CATEGORY)/*.rcp.cli.$(MAJOR).$(MINOR).jar $(A2_OUTPUT)/swt/rcp/$(A2_CATEGORY) + touch $(BUILD_BASE)/*.rcp/bnd.bnd + +A2_CATEGORY = org.argeo.cms BUNDLES = \ swt/rcp/org.argeo.swt.specific.rcp \ diff --git a/org.argeo.init/src/org/argeo/init/a2/A2Contribution.java b/org.argeo.init/src/org/argeo/init/a2/A2Contribution.java index 9d1348294..a4b720056 100644 --- a/org.argeo.init/src/org/argeo/init/a2/A2Contribution.java +++ b/org.argeo.init/src/org/argeo/init/a2/A2Contribution.java @@ -13,6 +13,8 @@ public class A2Contribution implements Comparable { final static String RUNTIME = "runtime"; final static String CLASSPATH = "classpath"; + final static String DEFAULT = "default"; + private final ProvisioningSource source; private final String id; 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 f43a6162a..0c3cefd01 100644 --- a/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java +++ b/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java @@ -122,6 +122,25 @@ public abstract class AbstractProvisioningSource implements ProvisioningSource { } + protected String[] readNameVersionFromModule(Path modulePath) { + Manifest manifest; + if (Files.isDirectory(modulePath)) { + manifest = findManifest(modulePath); + } else { + try (JarInputStream in = new JarInputStream(newInputStream(modulePath))) { + manifest = in.getManifest(); + } catch (IOException e) { + throw new A2Exception("Cannot read manifest from " + modulePath, e); + } + } + String versionStr = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION); + String symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME); + int semiColIndex = symbolicName.indexOf(';'); + if (semiColIndex >= 0) + symbolicName = symbolicName.substring(0, semiColIndex); + return new String[] { symbolicName, versionStr }; + } + protected String readVersionFromModule(Path modulePath) { Manifest manifest; if (Files.isDirectory(modulePath)) { 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 eb00659dc..5099eed15 100644 --- a/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java +++ b/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java @@ -7,8 +7,10 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.SortedSet; -import java.util.TreeSet; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; import org.argeo.init.osgi.OsgiBootUtils; import org.osgi.framework.Version; @@ -16,26 +18,72 @@ 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; public FsA2Source(Path base) { + this(base, new HashMap<>()); + } + + public FsA2Source(Path base, Map xOr) { this.base = base; + this.xOr = xOr; } void load() throws IOException { + SortedMap contributions = new TreeMap<>(); + DirectoryStream contributionPaths = Files.newDirectoryStream(base); - SortedSet contributions = new TreeSet<>(); contributions: for (Path contributionPath : contributionPaths) { if (Files.isDirectory(contributionPath)) { String contributionId = contributionPath.getFileName().toString(); if (A2Contribution.BOOT.equals(contributionId))// skip boot continue contributions; - A2Contribution contribution = getOrAddContribution(contributionId); - contributions.add(contribution); + if (contributionId.contains(".")) { + A2Contribution contribution = getOrAddContribution(contributionId); + contributions.put(contributionPath, contribution); + } else {// variants + Path variantPath = null; + // is it an explicit variant? + String variant = xOr.get(contributionPath.getFileName().toString()); + if (variant != null) { + variantPath = contributionPath.resolve(variant); + } + + // is there a default variant? + if (variantPath == null) { + Path defaultPath = contributionPath.resolve(A2Contribution.DEFAULT); + if (Files.exists(defaultPath)) { + variantPath = defaultPath; + } + } + + if (variantPath == null) + continue contributions; + + // a variant was found, let's collect its contributions (also common ones in its + // parent) + for (Path variantContributionPath : Files.newDirectoryStream(variantPath.getParent())) { + String variantContributionId = variantContributionPath.getFileName().toString(); + if (variantContributionId.contains(".")) { + A2Contribution contribution = getOrAddContribution(variantContributionId); + contributions.put(variantContributionPath, contribution); + } + } + for (Path variantContributionPath : Files.newDirectoryStream(variantPath)) { + String variantContributionId = variantContributionPath.getFileName().toString(); + if (variantContributionId.contains(".")) { + A2Contribution contribution = getOrAddContribution(variantContributionId); + contributions.put(variantContributionPath, contribution); + } + } + } } } - for (A2Contribution contribution : contributions) { - DirectoryStream modulePaths = Files.newDirectoryStream(base.resolve(contribution.getId())); + for (Path contributionPath : contributions.keySet()) { + String contributionId = contributionPath.getFileName().toString(); + A2Contribution contribution = getOrAddContribution(contributionId); + DirectoryStream modulePaths = Files.newDirectoryStream(contributionPath); modules: for (Path modulePath : modulePaths) { if (!Files.isDirectory(modulePath)) { // OsgiBootUtils.debug("Registering " + modulePath); @@ -57,8 +105,9 @@ public class FsA2Source extends AbstractProvisioningSource implements A2Source { // try { // version = new Version(versionStr); // } catch (Exception e) { - String versionStr = readVersionFromModule(modulePath); - String componentName = readSymbolicNameFromModule(modulePath); + String[] nameVersion = readNameVersionFromModule(modulePath); + String componentName = nameVersion[0]; + String versionStr = nameVersion[1]; if (versionStr != null) { version = new Version(versionStr); } else { @@ -91,9 +140,13 @@ public class FsA2Source extends AbstractProvisioningSource implements A2Source { } public static void main(String[] args) { + if (args.length == 0) + throw new IllegalArgumentException("Usage: "); try { - FsA2Source context = new FsA2Source(Paths.get( - "/home/mbaudier/dev/git/apache2/argeo-commons/dist/argeo-node/target/argeo-node-2.1.77-SNAPSHOT/share/osgi")); + 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) { 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 6012c34ee..6d842650c 100644 --- a/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java +++ b/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java @@ -1,15 +1,21 @@ package org.argeo.init.a2; import java.io.File; +import java.io.UnsupportedEncodingException; import java.net.URI; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; 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; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -55,6 +61,17 @@ public class ProvisioningManager { public void registerSource(String uri) { try { URI u = new URI(uri); + + // XOR + Map> properties = queryToMap(u); + Map xOr = new HashMap<>(); + for (String key : properties.keySet()) { + List lst = properties.get(key); + if (lst.size() != 1) + throw new IllegalArgumentException("Invalid XOR definitions in " + uri); + xOr.put(key, lst.get(0)); + } + if (A2Source.SCHEME_A2.equals(u.getScheme())) { if (u.getHost() == null || "".equals(u.getHost())) { String baseStr = u.getPath(); @@ -63,7 +80,7 @@ public class ProvisioningManager { } Path base = Paths.get(baseStr); if (Files.exists(base)) { - FsA2Source source = new FsA2Source(base); + FsA2Source source = new FsA2Source(base, xOr); source.load(); addSource(source); OsgiBootUtils.info("Registered " + uri + " as source"); @@ -172,25 +189,70 @@ public class ProvisioningManager { return updatedBundles; } + private static Map> queryToMap(URI uri) { + return queryToMap(uri.getQuery()); + } + + private static Map> queryToMap(String queryPart) { + try { + final Map> query_pairs = new LinkedHashMap>(); + if (queryPart == null) + return query_pairs; + final String[] pairs = queryPart.split("&"); + for (String pair : pairs) { + final int idx = pair.indexOf("="); + final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8.name()) + : pair; + if (!query_pairs.containsKey(key)) { + query_pairs.put(key, new LinkedList()); + } + final String value = idx > 0 && pair.length() > idx + 1 + ? URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8.name()) + : null; + query_pairs.get(key).add(value); + } + return query_pairs; + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Cannot convert " + queryPart + " to map", e); + } + } + 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()); - FsA2Source context = new FsA2Source(Paths.get( - "/home/mbaudier/dev/git/apache2/argeo-commons/dist/argeo-node/target/argeo-node-2.1.74-SNAPSHOT/argeo-node/share/osgi")); + 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(); + 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 ae343f05b..353e39c79 100644 --- a/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java @@ -102,11 +102,16 @@ public class OsgiBoot implements OsgiBootConstants { } else { for (String source : sources.split(",")) { if (source.trim().equals(A2Source.DEFAULT_A2_URI)) { + int qmIndex = source.lastIndexOf('?'); + String queryPart = ""; + if (qmIndex >= 0) { + queryPart = source.substring(qmIndex); + } if (Files.exists(homePath)) - provisioningManager - .registerSource(A2Source.SCHEME_A2 + "://" + homePath.toString() + "/.local/share/a2"); - provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/share/a2"); - provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/share/a2"); + provisioningManager.registerSource( + 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 { provisioningManager.registerSource(source); } -- 2.30.2