A2 variants supported
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 14 Jul 2022 16:11:23 +0000 (18:11 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 14 Jul 2022 16:11:23 +0000 (18:11 +0200)
Makefile
Makefile-rcp.mk
org.argeo.init/src/org/argeo/init/a2/A2Contribution.java
org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java
org.argeo.init/src/org/argeo/init/a2/FsA2Source.java
org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java
org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java

index 7129324bef247949a6fe97f5310dfb50838516b7..125205d5780122554755ba81e95393ca33bb04c3 100644 (file)
--- 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
index 6dfa39824315a31c82f0c60672f174bbda642f08..f09721089c19e71d068d7417b6c9b81ee2f3e325 100644 (file)
@@ -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 \
index 9d1348294925c1d8a0d6be4ddcb4006eeaab1fe3..a4b720056fda21036a2946f449add9b7e7622985 100644 (file)
@@ -13,6 +13,8 @@ public class A2Contribution implements Comparable<A2Contribution> {
        final static String RUNTIME = "runtime";
        final static String CLASSPATH = "classpath";
 
+       final static String DEFAULT = "default";
+
        private final ProvisioningSource source;
        private final String id;
 
index f43a6162a26b3a303b66792d9d980a32551ef1da..0c3cefd014a915f7a02d0c5b8caf69c858a0b6e1 100644 (file)
@@ -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)) {
index eb00659dcb4b7db84d381a1d5f86642a6c8d215f..5099eed15be9509ec516a44223dc095b07114025 100644 (file)
@@ -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<String, String> xOr;
 
        public FsA2Source(Path base) {
+               this(base, new HashMap<>());
+       }
+
+       public FsA2Source(Path base, Map<String, String> xOr) {
                this.base = base;
+               this.xOr = xOr;
        }
 
        void load() throws IOException {
+               SortedMap<Path, A2Contribution> contributions = new TreeMap<>();
+
                DirectoryStream<Path> contributionPaths = Files.newDirectoryStream(base);
-               SortedSet<A2Contribution> 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<Path> modulePaths = Files.newDirectoryStream(base.resolve(contribution.getId()));
+               for (Path contributionPath : contributions.keySet()) {
+                       String contributionId = contributionPath.getFileName().toString();
+                       A2Contribution contribution = getOrAddContribution(contributionId);
+                       DirectoryStream<Path> 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: <path to A2 base>");
                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<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) {
index 6012c34ee6d20afc9fc954cbe5a4005890c912c4..6d842650c2620fd2d26249bf69d7d741a37aa3fd 100644 (file)
@@ -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<String, List<String>> properties = queryToMap(u);
+                       Map<String, String> xOr = new HashMap<>();
+                       for (String key : properties.keySet()) {
+                               List<String> 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<String, List<String>> queryToMap(URI uri) {
+               return queryToMap(uri.getQuery());
+       }
+
+       private static Map<String, List<String>> queryToMap(String queryPart) {
+               try {
+                       final Map<String, List<String>> query_pairs = new LinkedHashMap<String, List<String>>();
+                       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<String>());
+                               }
+                               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: <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());
-                       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<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();
+                               framework.stop();
                        } catch (Exception e) {
                                e.printStackTrace();
                        }
index ae343f05b606a472e28beb492584cddfa4f7da01..353e39c798ed2e69c23852a46dc6a49f474ab717 100644 (file)
@@ -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);
                                }