--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.io.IOException;
+
+import org.argeo.osgi.a2.ClasspathSource;
+import org.argeo.osgi.a2.ProvisioningManager;
+import org.argeo.osgi.boot.equinox.EquinoxUtils;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+
+public class ClasspathSourceTest {
+ @Test
+ public void testProvisioning() throws IOException {
+ Framework framework = EquinoxUtils.launch(null);
+ ProvisioningManager provisioningManager = new ProvisioningManager(framework.getBundleContext());
+ ClasspathSource classpathSource = new ClasspathSource();
+ classpathSource.load();
+ provisioningManager.addSource(classpathSource);
+ provisioningManager.install(null);
+ for (Bundle bundle : framework.getBundleContext().getBundles()) {
+ System.out.println(bundle.getSymbolicName() + ":" + bundle.getVersion());
+ }
+ try {
+ framework.stop();
+ } catch (BundleException e) {
+ // silent
+ }
+ }
+}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.io.IOException;
-
-import org.argeo.osgi.boot.equinox.EquinoxUtils;
-import org.junit.Test;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.launch.Framework;
-
-public class ClasspathSourceTest {
- @Test
- public void testProvisioning() throws IOException {
- Framework framework = EquinoxUtils.launch(null);
- ProvisioningManager provisioningManager = new ProvisioningManager(framework.getBundleContext());
- ClasspathSource classpathSource = new ClasspathSource();
- classpathSource.load();
- provisioningManager.addSource(classpathSource);
- provisioningManager.install(null);
- for (Bundle bundle : framework.getBundleContext().getBundles()) {
- System.out.println(bundle.getSymbolicName() + ":" + bundle.getVersion());
- }
- try {
- framework.stop();
- } catch (BundleException e) {
- // silent
- }
- }
-}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.argeo.osgi.boot.OsgiBootUtils;
+import org.osgi.framework.Version;
+
+/**
+ * A logical linear sequence of versions of a given {@link A2Component}. This is
+ * typically a combination of major and minor version, indicating backward
+ * compatibility.
+ */
+public class A2Branch implements Comparable<A2Branch> {
+ private final A2Component component;
+ private final String id;
+
+ final SortedMap<Version, A2Module> modules = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ public A2Branch(A2Component component, String id) {
+ this.component = component;
+ this.id = id;
+ component.branches.put(id, this);
+ }
+
+ A2Module getOrAddModule(Version version, Object locator) {
+ if (modules.containsKey(version)) {
+ A2Module res = modules.get(version);
+ if (OsgiBootUtils.isDebug() && !res.getLocator().equals(locator)) {
+ OsgiBootUtils.debug("Inconsistent locator " + locator + " (registered: " + res.getLocator() + ")");
+ }
+ return res;
+ } else
+ return new A2Module(this, version, locator);
+ }
+
+ A2Module last() {
+ return modules.get(modules.lastKey());
+ }
+
+ A2Module first() {
+ return modules.get(modules.firstKey());
+ }
+
+ A2Component getComponent() {
+ return component;
+ }
+
+ String getId() {
+ return id;
+ }
+
+ @Override
+ public int compareTo(A2Branch o) {
+ return id.compareTo(id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof A2Branch) {
+ A2Branch o = (A2Branch) obj;
+ return component.equals(o.component) && id.equals(o.id);
+ } else
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return getCoordinates();
+ }
+
+ public String getCoordinates() {
+ return component + ":" + id;
+ }
+
+ static String versionToBranchId(Version version) {
+ return version.getMajor() + "." + version.getMinor();
+ }
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.osgi.framework.Version;
+
+/**
+ * The logical name of a software package. In OSGi's case this is
+ * <code>Bundle-SymbolicName</code>. This is the equivalent of Maven's artifact
+ * id.
+ */
+public class A2Component implements Comparable<A2Component> {
+ private final A2Contribution contribution;
+ private final String id;
+
+ final SortedMap<String, A2Branch> branches = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ public A2Component(A2Contribution contribution, String id) {
+ this.contribution = contribution;
+ this.id = id;
+ contribution.components.put(id, this);
+ }
+
+ A2Branch getOrAddBranch(String branchId) {
+ if (branches.containsKey(branchId))
+ return branches.get(branchId);
+ else
+ return new A2Branch(this, branchId);
+ }
+
+ A2Module getOrAddModule(Version version, Object locator) {
+ A2Branch branch = getOrAddBranch(A2Branch.versionToBranchId(version));
+ A2Module module = branch.getOrAddModule(version, locator);
+ return module;
+ }
+
+ A2Branch last() {
+ return branches.get(branches.lastKey());
+ }
+
+ A2Contribution getContribution() {
+ return contribution;
+ }
+
+ String getId() {
+ return id;
+ }
+
+ @Override
+ public int compareTo(A2Component o) {
+ return id.compareTo(o.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof A2Component) {
+ A2Component o = (A2Component) obj;
+ return contribution.equals(o.contribution) && id.equals(o.id);
+ } else
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return contribution.getId() + ":" + id;
+ }
+
+ void asTree(String prefix, StringBuffer buf) {
+ if (prefix == null)
+ prefix = "";
+ A2Branch lastBranch = last();
+ SortedMap<String, A2Branch> displayMap = new TreeMap<>(Collections.reverseOrder());
+ displayMap.putAll(branches);
+ for (String branchId : displayMap.keySet()) {
+ A2Branch branch = displayMap.get(branchId);
+ if (!lastBranch.equals(branch)) {
+ buf.append('\n');
+ buf.append(prefix);
+ } else {
+ buf.append(" -");
+ }
+ buf.append(prefix);
+ buf.append(branchId);
+ A2Module first = branch.first();
+ A2Module last = branch.last();
+ buf.append(" (").append(last.getVersion());
+ if (!first.equals(last))
+ buf.append(" ... ").append(first.getVersion());
+ buf.append(')');
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A category grouping a set of {@link A2Component}, typically based on the
+ * provider of these components. This is the equivalent of Maven's group Id.
+ */
+public class A2Contribution implements Comparable<A2Contribution> {
+ final static String BOOT = "boot";
+ final static String RUNTIME = "runtime";
+ final static String CLASSPATH = "classpath";
+
+ private final ProvisioningSource source;
+ private final String id;
+
+ final Map<String, A2Component> components = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ /**
+ * The contribution must be added to the source. Rather use
+ * {@link AbstractProvisioningSource#getOrAddContribution(String)} than this
+ * contructor directly.
+ */
+ public A2Contribution(ProvisioningSource context, String id) {
+ this.source = context;
+ this.id = id;
+// if (context != null)
+// context.contributions.put(id, this);
+ }
+
+ A2Component getOrAddComponent(String componentId) {
+ if (components.containsKey(componentId))
+ return components.get(componentId);
+ else
+ return new A2Component(this, componentId);
+ }
+
+ public ProvisioningSource getSource() {
+ return source;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ @Override
+ public int compareTo(A2Contribution o) {
+ return id.compareTo(o.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof A2Contribution) {
+ A2Contribution o = (A2Contribution) obj;
+ return id.equals(o.id);
+ } else
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return id;
+ }
+
+ void asTree(String prefix, StringBuffer buf) {
+ if (prefix == null)
+ prefix = "";
+ for (String componentId : components.keySet()) {
+ buf.append(prefix);
+ buf.append(componentId);
+ A2Component component = components.get(componentId);
+ component.asTree(prefix, buf);
+ buf.append('\n');
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+/** Unchecked A2 provisioning exception. */
+public class A2Exception extends RuntimeException {
+ private static final long serialVersionUID = 1927603558545397360L;
+
+ public A2Exception(String message, Throwable e) {
+ super(message, e);
+ }
+
+ public A2Exception(String message) {
+ super(message);
+ }
+
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import org.osgi.framework.Version;
+
+/**
+ * An identified software package. In OSGi's case this is the combination of
+ * <code>Bundle-SymbolicName</code> and <code>Bundle-version</code>. This is the
+ * equivalent of the full coordinates of a Maven artifact version.
+ */
+class A2Module implements Comparable<A2Module> {
+ private final A2Branch branch;
+ private final Version version;
+ private final Object locator;
+
+ public A2Module(A2Branch branch, Version version, Object locator) {
+ this.branch = branch;
+ this.version = version;
+ this.locator = locator;
+ branch.modules.put(version, this);
+ }
+
+ A2Branch getBranch() {
+ return branch;
+ }
+
+ Version getVersion() {
+ return version;
+ }
+
+ Object getLocator() {
+ return locator;
+ }
+
+ @Override
+ public int compareTo(A2Module o) {
+ return version.compareTo(o.version);
+ }
+
+ @Override
+ public int hashCode() {
+ return version.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof A2Module) {
+ A2Module o = (A2Module) obj;
+ return branch.equals(o.branch) && version.equals(o.version);
+ } else
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return getCoordinates();
+ }
+
+ public String getCoordinates() {
+ return branch.getComponent() + ":" + version;
+ }
+
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+/** A provisioning source in A2 format. */
+public interface A2Source extends ProvisioningSource {
+ final static String SCHEME_A2 = "a2";
+ final static String DEFAULT_A2_URI = SCHEME_A2 + ":///";
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Collections;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/** Where components are retrieved from. */
+public abstract class AbstractProvisioningSource implements ProvisioningSource {
+ protected final Map<String, A2Contribution> contributions = Collections.synchronizedSortedMap(new TreeMap<>());
+
+ public Iterable<A2Contribution> listContributions(Object filter) {
+ return contributions.values();
+ }
+
+ @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);
+ }
+ if (tempJar != null)
+ Files.deleteIfExists(tempJar);
+ return bundle;
+ } catch (BundleException | IOException e) {
+ throw new A2Exception("Cannot install module " + module, e);
+ }
+ }
+
+ @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);
+ }
+ if (tempJar != null)
+ Files.deleteIfExists(tempJar);
+ } catch (BundleException | IOException e) {
+ throw new A2Exception("Cannot update module " + module, e);
+ }
+ }
+
+ @Override
+ public A2Branch findBranch(String componentId, Version version) {
+ A2Component component = findComponent(componentId);
+ if (component == null)
+ return null;
+ String branchId = version.getMajor() + "." + version.getMinor();
+ if (!component.branches.containsKey(branchId))
+ return null;
+ return component.branches.get(branchId);
+ }
+
+ protected A2Contribution getOrAddContribution(String contributionId) {
+ if (contributions.containsKey(contributionId))
+ return contributions.get(contributionId);
+ else {
+ A2Contribution contribution = new A2Contribution(this, contributionId);
+ contributions.put(contributionId, contribution);
+ return contribution;
+ }
+ }
+
+ protected void asTree(String prefix, StringBuffer buf) {
+ if (prefix == null)
+ prefix = "";
+ for (String contributionId : contributions.keySet()) {
+ buf.append(prefix);
+ buf.append(contributionId);
+ buf.append('\n');
+ A2Contribution contribution = contributions.get(contributionId);
+ contribution.asTree(prefix + " ", buf);
+ }
+ }
+
+ protected void asTree() {
+ StringBuffer buf = new StringBuffer();
+ asTree("", buf);
+ System.out.println(buf);
+ }
+
+ protected A2Component findComponent(String componentId) {
+ SortedMap<A2Contribution, A2Component> res = new TreeMap<>();
+ for (A2Contribution contribution : contributions.values()) {
+ components: for (String componentIdKey : contribution.components.keySet()) {
+ if (componentId.equals(componentIdKey)) {
+ res.put(contribution, contribution.components.get(componentIdKey));
+ break components;
+ }
+ }
+ }
+ if (res.size() == 0)
+ return null;
+ // TODO explicit contribution priorities
+ return res.get(res.lastKey());
+
+ }
+
+ protected String readVersionFromModule(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);
+ return versionStr;
+ }
+
+ protected String readSymbolicNameFromModule(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 symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ int semiColIndex = symbolicName.indexOf(';');
+ if (semiColIndex >= 0)
+ symbolicName = symbolicName.substring(0, semiColIndex);
+ return symbolicName;
+ }
+
+ private static Manifest findManifest(Path currentPath) {
+ Path metaInfPath = currentPath.resolve("META-INF");
+ if (Files.exists(metaInfPath) && Files.isDirectory(metaInfPath)) {
+ Path manifestPath = metaInfPath.resolve("MANIFEST.MF");
+ try {
+ try (InputStream in = Files.newInputStream(manifestPath)) {
+ Manifest manifest = new Manifest(in);
+ return manifest;
+ }
+ } catch (IOException e) {
+ throw new A2Exception("Cannot read manifest from " + manifestPath, e);
+ }
+ } else {
+ Path parentPath = currentPath.getParent();
+ if (parentPath == null)
+ throw new A2Exception("MANIFEST.MF file not found.");
+ return findManifest(currentPath.getParent());
+ }
+ }
+
+ private static Path toTempJar(Path dir) {
+ try {
+ Manifest manifest = findManifest(dir);
+ Path jarPath = Files.createTempFile("a2Source", ".jar");
+ try (JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest)) {
+ Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Path relPath = dir.relativize(file);
+ // skip MANIFEST from folder
+ if (relPath.toString().contentEquals("META-INF/MANIFEST.MF"))
+ return FileVisitResult.CONTINUE;
+ zos.putNextEntry(new ZipEntry(relPath.toString()));
+ Files.copy(file, zos);
+ zos.closeEntry();
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+ return jarPath;
+ } catch (IOException e) {
+ throw new A2Exception("Cannot install OSGi bundle from " + dir, e);
+ }
+
+ }
+
+ private InputStream newInputStream(Object locator) throws IOException {
+ if (locator instanceof Path) {
+ return Files.newInputStream((Path) locator);
+ } else if (locator instanceof URL) {
+ return ((URL) locator).openStream();
+ } else {
+ throw new IllegalArgumentException("Unsupported module locator type " + locator.getClass());
+ }
+ }
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+
+import org.argeo.osgi.boot.OsgiBootUtils;
+import org.osgi.framework.Version;
+
+/**
+ * A provisioning source based on the linear classpath with which the JCM has
+ * been started.
+ */
+public class ClasspathSource extends AbstractProvisioningSource {
+ void load() throws IOException {
+ A2Contribution classpathContribution = getOrAddContribution( A2Contribution.CLASSPATH);
+ List<String> classpath = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator));
+ parts: for (String part : classpath) {
+ Path file = Paths.get(part);
+ Version version;
+ try {
+ version = new Version(readVersionFromModule(file));
+ } catch (Exception e) {
+ // ignore non OSGi
+ continue parts;
+ }
+ String moduleName = readSymbolicNameFromModule(file);
+ A2Component component = classpathContribution.getOrAddComponent(moduleName);
+ A2Module module = component.getOrAddModule(version, file);
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.debug("Registered " + module);
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.io.IOException;
+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 org.argeo.osgi.boot.OsgiBootUtils;
+import org.osgi.framework.Version;
+
+/** A file system {@link AbstractProvisioningSource} in A2 format. */
+public class FsA2Source extends AbstractProvisioningSource implements A2Source {
+ private final Path base;
+
+ public FsA2Source(Path base) {
+ super();
+ this.base = base;
+ }
+
+ void load() throws IOException {
+ 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);
+ }
+ }
+
+ for (A2Contribution contribution : contributions) {
+ DirectoryStream<Path> modulePaths = Files.newDirectoryStream(base.resolve(contribution.getId()));
+ modules: for (Path modulePath : modulePaths) {
+ if (!Files.isDirectory(modulePath)) {
+ // OsgiBootUtils.debug("Registering " + modulePath);
+ String moduleFileName = modulePath.getFileName().toString();
+ int lastDot = moduleFileName.lastIndexOf('.');
+ 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) {
+ versionStr = readVersionFromModule(modulePath);
+ if (versionStr != null) {
+ version = new Version(versionStr);
+ } else {
+ OsgiBootUtils.debug("Ignore " + modulePath + " (" + e.getMessage() + ")");
+ continue modules;
+ }
+ }
+ A2Component component = contribution.getOrAddComponent(componentName);
+ A2Module module = component.getOrAddModule(version, modulePath);
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.debug("Registered " + module);
+ }
+ }
+ }
+
+ }
+
+ public static void main(String[] args) {
+ 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"));
+ context.load();
+ context.asTree();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.argeo.osgi.boot.OsgiBootUtils;
+import org.osgi.framework.Version;
+
+/** A file system {@link AbstractProvisioningSource} in Maven 2 format. */
+public class FsM2Source extends AbstractProvisioningSource {
+ private final Path base;
+
+ public FsM2Source(Path base) {
+ super();
+ this.base = base;
+ }
+
+ void load() throws IOException {
+ Files.walkFileTree(base, new ArtifactFileVisitor());
+ }
+
+ class ArtifactFileVisitor extends SimpleFileVisitor<Path> {
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ // OsgiBootUtils.debug("Processing " + file);
+ if (file.toString().endsWith(".jar")) {
+ Version version;
+ try {
+ version = new Version(readVersionFromModule(file));
+ } catch (Exception e) {
+ // ignore non OSGi
+ return FileVisitResult.CONTINUE;
+ }
+ String moduleName = readSymbolicNameFromModule(file);
+ Path groupPath = file.getParent().getParent().getParent();
+ Path relGroupPath = base.relativize(groupPath);
+ String contributionName = relGroupPath.toString().replace(File.separatorChar, '.');
+ A2Contribution contribution = getOrAddContribution(contributionName);
+ A2Component component = contribution.getOrAddComponent(moduleName);
+ A2Module module = component.getOrAddModule(version, file);
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.debug("Registered " + module);
+ }
+ return super.visitFile(file, attrs);
+ }
+
+ }
+
+ public static void main(String[] args) {
+ try {
+ FsM2Source context = new FsM2Source(Paths.get("/home/mbaudier/.m2/repository"));
+ context.load();
+ context.asTree();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import org.argeo.osgi.boot.OsgiBootUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.Version;
+
+/** A running OSGi bundle context seen as a {@link AbstractProvisioningSource}. */
+class OsgiContext extends AbstractProvisioningSource {
+ private final BundleContext bc;
+
+ public OsgiContext(BundleContext bc) {
+ super();
+ this.bc = bc;
+ }
+
+ public OsgiContext() {
+ Bundle bundle = FrameworkUtil.getBundle(OsgiContext.class);
+ if (bundle == null)
+ throw new IllegalArgumentException(
+ "OSGi Boot bundle must be started or a bundle context must be specified");
+ this.bc = bundle.getBundleContext();
+ }
+
+ void load() {
+ A2Contribution runtimeContribution = getOrAddContribution( A2Contribution.RUNTIME);
+ for (Bundle bundle : bc.getBundles()) {
+ // OsgiBootUtils.debug(bundle.getDataFile("/"));
+ String componentId = bundle.getSymbolicName();
+ Version version = bundle.getVersion();
+ A2Component component = runtimeContribution.getOrAddComponent(componentId);
+ A2Module module = component.getOrAddModule(version, bundle);
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.debug("Registered " + module + " (location id: " + bundle.getLocation() + ")");
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import java.io.File;
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.argeo.osgi.boot.OsgiBootUtils;
+import org.eclipse.osgi.launch.EquinoxFactory;
+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. */
+public class ProvisioningManager {
+ BundleContext bc;
+ OsgiContext osgiContext;
+ List<ProvisioningSource> sources = Collections.synchronizedList(new ArrayList<>());
+
+ public ProvisioningManager(BundleContext bc) {
+ this.bc = bc;
+ osgiContext = new OsgiContext(bc);
+ osgiContext.load();
+ }
+
+ protected void addSource(ProvisioningSource source) {
+ sources.add(source);
+ }
+
+ void installWholeSource(ProvisioningSource source) {
+ Set<Bundle> updatedBundles = new HashSet<>();
+ for (A2Contribution contribution : source.listContributions(null)) {
+ for (A2Component component : contribution.components.values()) {
+ A2Module module = component.last().last();
+ Bundle bundle = installOrUpdate(module);
+ if (bundle != null)
+ updatedBundles.add(bundle);
+ }
+ }
+ FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
+ frameworkWiring.refreshBundles(updatedBundles);
+ }
+
+ public void registerSource(String uri) {
+ try {
+ URI u = new URI(uri);
+ if (A2Source.SCHEME_A2.equals(u.getScheme())) {
+ if (u.getHost() == null || "".equals(u.getHost())) {
+ String baseStr = u.getPath();
+ if (File.separatorChar == '\\') {// MS Windows
+ baseStr = baseStr.substring(1).replace('/', File.separatorChar);
+ }
+ Path base = Paths.get(baseStr);
+ FsA2Source source = new FsA2Source(base);
+ source.load();
+ addSource(source);
+ }
+ }
+ } catch (Exception e) {
+ throw new A2Exception("Cannot add source " + uri, e);
+ }
+ }
+
+ public boolean registerDefaultSource() {
+ String frameworkLocation = bc.getProperty("osgi.framework");
+ try {
+ URI frameworkLocationUri = new URI(frameworkLocation);
+ if ("file".equals(frameworkLocationUri.getScheme())) {
+ Path frameworkPath = Paths.get(frameworkLocationUri);
+ if (frameworkPath.getParent().getFileName().toString().equals(A2Contribution.BOOT)) {
+ Path base = frameworkPath.getParent().getParent();
+ String baseStr = base.toString();
+ if (File.separatorChar == '\\')// MS Windows
+ baseStr = '/' + baseStr.replace(File.separatorChar, '/');
+ URI baseUri = new URI(A2Source.SCHEME_A2, null, null, 0, baseStr, null, null);
+ registerSource(baseUri.toString());
+ OsgiBootUtils.info("Registered " + baseUri + " as default source");
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ OsgiBootUtils.error("Cannot register default source based on framework location " + frameworkLocation, e);
+ }
+ return false;
+ }
+
+ public void install(String spec) {
+ if (spec == null) {
+ for (ProvisioningSource source : sources) {
+ installWholeSource(source);
+ }
+ }
+ }
+
+ /** @return the new/updated bundle, or null if nothing was done. */
+ protected Bundle installOrUpdate(A2Module module) {
+ try {
+ ProvisioningSource moduleSource = module.getBranch().getComponent().getContribution().getSource();
+ Version moduleVersion = module.getVersion();
+ A2Branch osgiBranch = osgiContext.findBranch(module.getBranch().getComponent().getId(), moduleVersion);
+ if (osgiBranch == null) {
+// Bundle bundle = bc.installBundle(module.getBranch().getCoordinates(),
+// moduleSource.newInputStream(module.getLocator()));
+ Bundle bundle = moduleSource.install(bc, module);
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.debug("Installed bundle " + bundle.getLocation() + " with version " + moduleVersion);
+ return bundle;
+ } else {
+ A2Module lastOsgiModule = osgiBranch.last();
+ int compare = moduleVersion.compareTo(lastOsgiModule.getVersion());
+ if (compare > 0) {// update
+ Bundle bundle = (Bundle) lastOsgiModule.getLocator();
+// bundle.update(moduleSource.newInputStream(module.getLocator()));
+ moduleSource.update(bundle, module);
+ OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
+ return bundle;
+ }
+ }
+ } catch (Exception e) {
+ OsgiBootUtils.error("Could not install module " + module + ": " + e.getMessage(), e);
+ }
+ return null;
+ }
+
+ public Collection<Bundle> update() {
+ boolean fragmentsUpdated = false;
+ Set<Bundle> updatedBundles = new HashSet<>();
+ bundles: for (Bundle bundle : bc.getBundles()) {
+ for (ProvisioningSource source : sources) {
+ String componentId = bundle.getSymbolicName();
+ Version version = bundle.getVersion();
+ A2Branch branch = source.findBranch(componentId, version);
+ if (branch == null)
+ continue bundles;
+ A2Module module = branch.last();
+ Version moduleVersion = module.getVersion();
+ int compare = moduleVersion.compareTo(version);
+ if (compare > 0) {// update
+ try {
+ source.update(bundle, module);
+// bundle.update(in);
+ String fragmentHost = bundle.getHeaders().get(Constants.FRAGMENT_HOST);
+ if (fragmentHost != null)
+ fragmentsUpdated = true;
+ OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
+ updatedBundles.add(bundle);
+ } catch (Exception e) {
+ OsgiBootUtils.error("Cannot update with module " + module, e);
+ }
+ }
+ }
+ }
+ FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
+ if (fragmentsUpdated)// refresh all
+ frameworkWiring.refreshBundles(null);
+ else
+ frameworkWiring.refreshBundles(updatedBundles);
+ return updatedBundles;
+ }
+
+ public static void main(String[] args) {
+ Map<String, String> configuration = new HashMap<>();
+ configuration.put("osgi.console", "2323");
+ Framework framework = OsgiBootUtils.launch(new EquinoxFactory(), 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"));
+ context.load();
+ if (framework.getBundleContext().getBundles().length == 1) {// initial
+ pm.install(null);
+ } else {
+ pm.update();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ // framework.stop();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.osgi.a2;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+
+/** Where components are retrieved from. */
+public interface ProvisioningSource {
+ /** List all contributions of this source. */
+ Iterable<A2Contribution> listContributions(Object filter);
+
+ /** Install a module in the OSGi runtime. */
+ Bundle install(BundleContext bc, A2Module module);
+
+ /** Update a module in the OSGi runtime. */
+ void update(Bundle bundle, A2Module module);
+
+ /** Finds the {@link A2Branch} related to this component and version. */
+ A2Branch findBranch(String componentId, Version version);
+
+}
import java.util.StringTokenizer;
import java.util.TreeMap;
-import org.argeo.osgi.boot.a2.A2Source;
-import org.argeo.osgi.boot.a2.ProvisioningManager;
+import org.argeo.osgi.a2.A2Source;
+import org.argeo.osgi.a2.ProvisioningManager;
import org.argeo.osgi.boot.internal.springutil.AntPathMatcher;
import org.argeo.osgi.boot.internal.springutil.PathMatcher;
import org.argeo.osgi.boot.internal.springutil.SystemPropertyUtils;
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.util.Collections;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.argeo.osgi.boot.OsgiBootUtils;
-import org.osgi.framework.Version;
-
-/**
- * A logical linear sequence of versions of a given {@link A2Component}. This is
- * typically a combination of major and minor version, indicating backward
- * compatibility.
- */
-public class A2Branch implements Comparable<A2Branch> {
- private final A2Component component;
- private final String id;
-
- final SortedMap<Version, A2Module> modules = Collections.synchronizedSortedMap(new TreeMap<>());
-
- public A2Branch(A2Component component, String id) {
- this.component = component;
- this.id = id;
- component.branches.put(id, this);
- }
-
- A2Module getOrAddModule(Version version, Object locator) {
- if (modules.containsKey(version)) {
- A2Module res = modules.get(version);
- if (OsgiBootUtils.isDebug() && !res.getLocator().equals(locator)) {
- OsgiBootUtils.debug("Inconsistent locator " + locator + " (registered: " + res.getLocator() + ")");
- }
- return res;
- } else
- return new A2Module(this, version, locator);
- }
-
- A2Module last() {
- return modules.get(modules.lastKey());
- }
-
- A2Module first() {
- return modules.get(modules.firstKey());
- }
-
- A2Component getComponent() {
- return component;
- }
-
- String getId() {
- return id;
- }
-
- @Override
- public int compareTo(A2Branch o) {
- return id.compareTo(id);
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof A2Branch) {
- A2Branch o = (A2Branch) obj;
- return component.equals(o.component) && id.equals(o.id);
- } else
- return false;
- }
-
- @Override
- public String toString() {
- return getCoordinates();
- }
-
- public String getCoordinates() {
- return component + ":" + id;
- }
-
- static String versionToBranchId(Version version) {
- return version.getMajor() + "." + version.getMinor();
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.util.Collections;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import org.osgi.framework.Version;
-
-/**
- * The logical name of a software package. In OSGi's case this is
- * <code>Bundle-SymbolicName</code>. This is the equivalent of Maven's artifact
- * id.
- */
-public class A2Component implements Comparable<A2Component> {
- private final A2Contribution contribution;
- private final String id;
-
- final SortedMap<String, A2Branch> branches = Collections.synchronizedSortedMap(new TreeMap<>());
-
- public A2Component(A2Contribution contribution, String id) {
- this.contribution = contribution;
- this.id = id;
- contribution.components.put(id, this);
- }
-
- A2Branch getOrAddBranch(String branchId) {
- if (branches.containsKey(branchId))
- return branches.get(branchId);
- else
- return new A2Branch(this, branchId);
- }
-
- A2Module getOrAddModule(Version version, Object locator) {
- A2Branch branch = getOrAddBranch(A2Branch.versionToBranchId(version));
- A2Module module = branch.getOrAddModule(version, locator);
- return module;
- }
-
- A2Branch last() {
- return branches.get(branches.lastKey());
- }
-
- A2Contribution getContribution() {
- return contribution;
- }
-
- String getId() {
- return id;
- }
-
- @Override
- public int compareTo(A2Component o) {
- return id.compareTo(o.id);
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof A2Component) {
- A2Component o = (A2Component) obj;
- return contribution.equals(o.contribution) && id.equals(o.id);
- } else
- return false;
- }
-
- @Override
- public String toString() {
- return contribution.getId() + ":" + id;
- }
-
- void asTree(String prefix, StringBuffer buf) {
- if (prefix == null)
- prefix = "";
- A2Branch lastBranch = last();
- SortedMap<String, A2Branch> displayMap = new TreeMap<>(Collections.reverseOrder());
- displayMap.putAll(branches);
- for (String branchId : displayMap.keySet()) {
- A2Branch branch = displayMap.get(branchId);
- if (!lastBranch.equals(branch)) {
- buf.append('\n');
- buf.append(prefix);
- } else {
- buf.append(" -");
- }
- buf.append(prefix);
- buf.append(branchId);
- A2Module first = branch.first();
- A2Module last = branch.last();
- buf.append(" (").append(last.getVersion());
- if (!first.equals(last))
- buf.append(" ... ").append(first.getVersion());
- buf.append(')');
- }
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * A category grouping a set of {@link A2Component}, typically based on the
- * provider of these components. This is the equivalent of Maven's group Id.
- */
-public class A2Contribution implements Comparable<A2Contribution> {
- final static String BOOT = "boot";
- final static String RUNTIME = "runtime";
- final static String CLASSPATH = "classpath";
-
- private final ProvisioningSource source;
- private final String id;
-
- final Map<String, A2Component> components = Collections.synchronizedSortedMap(new TreeMap<>());
-
- /**
- * The contribution must be added to the source. Rather use
- * {@link AbstractProvisioningSource#getOrAddContribution(String)} than this
- * contructor directly.
- */
- public A2Contribution(ProvisioningSource context, String id) {
- this.source = context;
- this.id = id;
-// if (context != null)
-// context.contributions.put(id, this);
- }
-
- A2Component getOrAddComponent(String componentId) {
- if (components.containsKey(componentId))
- return components.get(componentId);
- else
- return new A2Component(this, componentId);
- }
-
- public ProvisioningSource getSource() {
- return source;
- }
-
- public String getId() {
- return id;
- }
-
- @Override
- public int compareTo(A2Contribution o) {
- return id.compareTo(o.id);
- }
-
- @Override
- public int hashCode() {
- return id.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof A2Contribution) {
- A2Contribution o = (A2Contribution) obj;
- return id.equals(o.id);
- } else
- return false;
- }
-
- @Override
- public String toString() {
- return id;
- }
-
- void asTree(String prefix, StringBuffer buf) {
- if (prefix == null)
- prefix = "";
- for (String componentId : components.keySet()) {
- buf.append(prefix);
- buf.append(componentId);
- A2Component component = components.get(componentId);
- component.asTree(prefix, buf);
- buf.append('\n');
- }
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-/** Unchecked A2 provisioning exception. */
-public class A2Exception extends RuntimeException {
- private static final long serialVersionUID = 1927603558545397360L;
-
- public A2Exception(String message, Throwable e) {
- super(message, e);
- }
-
- public A2Exception(String message) {
- super(message);
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import org.osgi.framework.Version;
-
-/**
- * An identified software package. In OSGi's case this is the combination of
- * <code>Bundle-SymbolicName</code> and <code>Bundle-version</code>. This is the
- * equivalent of the full coordinates of a Maven artifact version.
- */
-class A2Module implements Comparable<A2Module> {
- private final A2Branch branch;
- private final Version version;
- private final Object locator;
-
- public A2Module(A2Branch branch, Version version, Object locator) {
- this.branch = branch;
- this.version = version;
- this.locator = locator;
- branch.modules.put(version, this);
- }
-
- A2Branch getBranch() {
- return branch;
- }
-
- Version getVersion() {
- return version;
- }
-
- Object getLocator() {
- return locator;
- }
-
- @Override
- public int compareTo(A2Module o) {
- return version.compareTo(o.version);
- }
-
- @Override
- public int hashCode() {
- return version.hashCode();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof A2Module) {
- A2Module o = (A2Module) obj;
- return branch.equals(o.branch) && version.equals(o.version);
- } else
- return false;
- }
-
- @Override
- public String toString() {
- return getCoordinates();
- }
-
- public String getCoordinates() {
- return branch.getComponent() + ":" + version;
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-/** A provisioning source in A2 format. */
-public interface A2Source extends ProvisioningSource {
- final static String SCHEME_A2 = "a2";
- final static String DEFAULT_A2_URI = SCHEME_A2 + ":///";
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.Collections;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.jar.JarInputStream;
-import java.util.jar.JarOutputStream;
-import java.util.jar.Manifest;
-import java.util.zip.ZipEntry;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-
-/** Where components are retrieved from. */
-public abstract class AbstractProvisioningSource implements ProvisioningSource {
- protected final Map<String, A2Contribution> contributions = Collections.synchronizedSortedMap(new TreeMap<>());
-
- public Iterable<A2Contribution> listContributions(Object filter) {
- return contributions.values();
- }
-
- @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);
- }
- if (tempJar != null)
- Files.deleteIfExists(tempJar);
- return bundle;
- } catch (BundleException | IOException e) {
- throw new A2Exception("Cannot install module " + module, e);
- }
- }
-
- @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);
- }
- if (tempJar != null)
- Files.deleteIfExists(tempJar);
- } catch (BundleException | IOException e) {
- throw new A2Exception("Cannot update module " + module, e);
- }
- }
-
- @Override
- public A2Branch findBranch(String componentId, Version version) {
- A2Component component = findComponent(componentId);
- if (component == null)
- return null;
- String branchId = version.getMajor() + "." + version.getMinor();
- if (!component.branches.containsKey(branchId))
- return null;
- return component.branches.get(branchId);
- }
-
- protected A2Contribution getOrAddContribution(String contributionId) {
- if (contributions.containsKey(contributionId))
- return contributions.get(contributionId);
- else {
- A2Contribution contribution = new A2Contribution(this, contributionId);
- contributions.put(contributionId, contribution);
- return contribution;
- }
- }
-
- protected void asTree(String prefix, StringBuffer buf) {
- if (prefix == null)
- prefix = "";
- for (String contributionId : contributions.keySet()) {
- buf.append(prefix);
- buf.append(contributionId);
- buf.append('\n');
- A2Contribution contribution = contributions.get(contributionId);
- contribution.asTree(prefix + " ", buf);
- }
- }
-
- protected void asTree() {
- StringBuffer buf = new StringBuffer();
- asTree("", buf);
- System.out.println(buf);
- }
-
- protected A2Component findComponent(String componentId) {
- SortedMap<A2Contribution, A2Component> res = new TreeMap<>();
- for (A2Contribution contribution : contributions.values()) {
- components: for (String componentIdKey : contribution.components.keySet()) {
- if (componentId.equals(componentIdKey)) {
- res.put(contribution, contribution.components.get(componentIdKey));
- break components;
- }
- }
- }
- if (res.size() == 0)
- return null;
- // TODO explicit contribution priorities
- return res.get(res.lastKey());
-
- }
-
- protected String readVersionFromModule(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);
- return versionStr;
- }
-
- protected String readSymbolicNameFromModule(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 symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
- int semiColIndex = symbolicName.indexOf(';');
- if (semiColIndex >= 0)
- symbolicName = symbolicName.substring(0, semiColIndex);
- return symbolicName;
- }
-
- private static Manifest findManifest(Path currentPath) {
- Path metaInfPath = currentPath.resolve("META-INF");
- if (Files.exists(metaInfPath) && Files.isDirectory(metaInfPath)) {
- Path manifestPath = metaInfPath.resolve("MANIFEST.MF");
- try {
- try (InputStream in = Files.newInputStream(manifestPath)) {
- Manifest manifest = new Manifest(in);
- return manifest;
- }
- } catch (IOException e) {
- throw new A2Exception("Cannot read manifest from " + manifestPath, e);
- }
- } else {
- Path parentPath = currentPath.getParent();
- if (parentPath == null)
- throw new A2Exception("MANIFEST.MF file not found.");
- return findManifest(currentPath.getParent());
- }
- }
-
- private static Path toTempJar(Path dir) {
- try {
- Manifest manifest = findManifest(dir);
- Path jarPath = Files.createTempFile("a2Source", ".jar");
- try (JarOutputStream zos = new JarOutputStream(new FileOutputStream(jarPath.toFile()), manifest)) {
- Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- Path relPath = dir.relativize(file);
- // skip MANIFEST from folder
- if (relPath.toString().contentEquals("META-INF/MANIFEST.MF"))
- return FileVisitResult.CONTINUE;
- zos.putNextEntry(new ZipEntry(relPath.toString()));
- Files.copy(file, zos);
- zos.closeEntry();
- return FileVisitResult.CONTINUE;
- }
- });
- }
- return jarPath;
- } catch (IOException e) {
- throw new A2Exception("Cannot install OSGi bundle from " + dir, e);
- }
-
- }
-
- private InputStream newInputStream(Object locator) throws IOException {
- if (locator instanceof Path) {
- return Files.newInputStream((Path) locator);
- } else if (locator instanceof URL) {
- return ((URL) locator).openStream();
- } else {
- throw new IllegalArgumentException("Unsupported module locator type " + locator.getClass());
- }
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Arrays;
-import java.util.List;
-
-import org.argeo.osgi.boot.OsgiBootUtils;
-import org.osgi.framework.Version;
-
-/**
- * A provisioning source based on the linear classpath with which the JCM has
- * been started.
- */
-public class ClasspathSource extends AbstractProvisioningSource {
- void load() throws IOException {
- A2Contribution classpathContribution = getOrAddContribution( A2Contribution.CLASSPATH);
- List<String> classpath = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator));
- parts: for (String part : classpath) {
- Path file = Paths.get(part);
- Version version;
- try {
- version = new Version(readVersionFromModule(file));
- } catch (Exception e) {
- // ignore non OSGi
- continue parts;
- }
- String moduleName = readSymbolicNameFromModule(file);
- A2Component component = classpathContribution.getOrAddComponent(moduleName);
- A2Module module = component.getOrAddModule(version, file);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered " + module);
- }
-
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.io.IOException;
-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 org.argeo.osgi.boot.OsgiBootUtils;
-import org.osgi.framework.Version;
-
-/** A file system {@link AbstractProvisioningSource} in A2 format. */
-public class FsA2Source extends AbstractProvisioningSource implements A2Source {
- private final Path base;
-
- public FsA2Source(Path base) {
- super();
- this.base = base;
- }
-
- void load() throws IOException {
- 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);
- }
- }
-
- for (A2Contribution contribution : contributions) {
- DirectoryStream<Path> modulePaths = Files.newDirectoryStream(base.resolve(contribution.getId()));
- modules: for (Path modulePath : modulePaths) {
- if (!Files.isDirectory(modulePath)) {
- // OsgiBootUtils.debug("Registering " + modulePath);
- String moduleFileName = modulePath.getFileName().toString();
- int lastDot = moduleFileName.lastIndexOf('.');
- 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) {
- versionStr = readVersionFromModule(modulePath);
- if (versionStr != null) {
- version = new Version(versionStr);
- } else {
- OsgiBootUtils.debug("Ignore " + modulePath + " (" + e.getMessage() + ")");
- continue modules;
- }
- }
- A2Component component = contribution.getOrAddComponent(componentName);
- A2Module module = component.getOrAddModule(version, modulePath);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered " + module);
- }
- }
- }
-
- }
-
- public static void main(String[] args) {
- 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"));
- context.load();
- context.asTree();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.attribute.BasicFileAttributes;
-
-import org.argeo.osgi.boot.OsgiBootUtils;
-import org.osgi.framework.Version;
-
-/** A file system {@link AbstractProvisioningSource} in Maven 2 format. */
-public class FsM2Source extends AbstractProvisioningSource {
- private final Path base;
-
- public FsM2Source(Path base) {
- super();
- this.base = base;
- }
-
- void load() throws IOException {
- Files.walkFileTree(base, new ArtifactFileVisitor());
- }
-
- class ArtifactFileVisitor extends SimpleFileVisitor<Path> {
-
- @Override
- public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
- // OsgiBootUtils.debug("Processing " + file);
- if (file.toString().endsWith(".jar")) {
- Version version;
- try {
- version = new Version(readVersionFromModule(file));
- } catch (Exception e) {
- // ignore non OSGi
- return FileVisitResult.CONTINUE;
- }
- String moduleName = readSymbolicNameFromModule(file);
- Path groupPath = file.getParent().getParent().getParent();
- Path relGroupPath = base.relativize(groupPath);
- String contributionName = relGroupPath.toString().replace(File.separatorChar, '.');
- A2Contribution contribution = getOrAddContribution(contributionName);
- A2Component component = contribution.getOrAddComponent(moduleName);
- A2Module module = component.getOrAddModule(version, file);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered " + module);
- }
- return super.visitFile(file, attrs);
- }
-
- }
-
- public static void main(String[] args) {
- try {
- FsM2Source context = new FsM2Source(Paths.get("/home/mbaudier/.m2/repository"));
- context.load();
- context.asTree();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import org.argeo.osgi.boot.OsgiBootUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.Version;
-
-/** A running OSGi bundle context seen as a {@link AbstractProvisioningSource}. */
-class OsgiContext extends AbstractProvisioningSource {
- private final BundleContext bc;
-
- public OsgiContext(BundleContext bc) {
- super();
- this.bc = bc;
- }
-
- public OsgiContext() {
- Bundle bundle = FrameworkUtil.getBundle(OsgiContext.class);
- if (bundle == null)
- throw new IllegalArgumentException(
- "OSGi Boot bundle must be started or a bundle context must be specified");
- this.bc = bundle.getBundleContext();
- }
-
- void load() {
- A2Contribution runtimeContribution = getOrAddContribution( A2Contribution.RUNTIME);
- for (Bundle bundle : bc.getBundles()) {
- // OsgiBootUtils.debug(bundle.getDataFile("/"));
- String componentId = bundle.getSymbolicName();
- Version version = bundle.getVersion();
- A2Component component = runtimeContribution.getOrAddComponent(componentId);
- A2Module module = component.getOrAddModule(version, bundle);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Registered " + module + " (location id: " + bundle.getLocation() + ")");
- }
-
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import java.io.File;
-import java.net.URI;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.argeo.osgi.boot.OsgiBootUtils;
-import org.eclipse.osgi.launch.EquinoxFactory;
-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. */
-public class ProvisioningManager {
- BundleContext bc;
- OsgiContext osgiContext;
- List<ProvisioningSource> sources = Collections.synchronizedList(new ArrayList<>());
-
- public ProvisioningManager(BundleContext bc) {
- this.bc = bc;
- osgiContext = new OsgiContext(bc);
- osgiContext.load();
- }
-
- protected void addSource(ProvisioningSource source) {
- sources.add(source);
- }
-
- void installWholeSource(ProvisioningSource source) {
- Set<Bundle> updatedBundles = new HashSet<>();
- for (A2Contribution contribution : source.listContributions(null)) {
- for (A2Component component : contribution.components.values()) {
- A2Module module = component.last().last();
- Bundle bundle = installOrUpdate(module);
- if (bundle != null)
- updatedBundles.add(bundle);
- }
- }
- FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
- frameworkWiring.refreshBundles(updatedBundles);
- }
-
- public void registerSource(String uri) {
- try {
- URI u = new URI(uri);
- if (A2Source.SCHEME_A2.equals(u.getScheme())) {
- if (u.getHost() == null || "".equals(u.getHost())) {
- String baseStr = u.getPath();
- if (File.separatorChar == '\\') {// MS Windows
- baseStr = baseStr.substring(1).replace('/', File.separatorChar);
- }
- Path base = Paths.get(baseStr);
- FsA2Source source = new FsA2Source(base);
- source.load();
- addSource(source);
- }
- }
- } catch (Exception e) {
- throw new A2Exception("Cannot add source " + uri, e);
- }
- }
-
- public boolean registerDefaultSource() {
- String frameworkLocation = bc.getProperty("osgi.framework");
- try {
- URI frameworkLocationUri = new URI(frameworkLocation);
- if ("file".equals(frameworkLocationUri.getScheme())) {
- Path frameworkPath = Paths.get(frameworkLocationUri);
- if (frameworkPath.getParent().getFileName().toString().equals(A2Contribution.BOOT)) {
- Path base = frameworkPath.getParent().getParent();
- String baseStr = base.toString();
- if (File.separatorChar == '\\')// MS Windows
- baseStr = '/' + baseStr.replace(File.separatorChar, '/');
- URI baseUri = new URI(A2Source.SCHEME_A2, null, null, 0, baseStr, null, null);
- registerSource(baseUri.toString());
- OsgiBootUtils.info("Registered " + baseUri + " as default source");
- return true;
- }
- }
- } catch (Exception e) {
- OsgiBootUtils.error("Cannot register default source based on framework location " + frameworkLocation, e);
- }
- return false;
- }
-
- public void install(String spec) {
- if (spec == null) {
- for (ProvisioningSource source : sources) {
- installWholeSource(source);
- }
- }
- }
-
- /** @return the new/updated bundle, or null if nothing was done. */
- protected Bundle installOrUpdate(A2Module module) {
- try {
- ProvisioningSource moduleSource = module.getBranch().getComponent().getContribution().getSource();
- Version moduleVersion = module.getVersion();
- A2Branch osgiBranch = osgiContext.findBranch(module.getBranch().getComponent().getId(), moduleVersion);
- if (osgiBranch == null) {
-// Bundle bundle = bc.installBundle(module.getBranch().getCoordinates(),
-// moduleSource.newInputStream(module.getLocator()));
- Bundle bundle = moduleSource.install(bc, module);
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("Installed bundle " + bundle.getLocation() + " with version " + moduleVersion);
- return bundle;
- } else {
- A2Module lastOsgiModule = osgiBranch.last();
- int compare = moduleVersion.compareTo(lastOsgiModule.getVersion());
- if (compare > 0) {// update
- Bundle bundle = (Bundle) lastOsgiModule.getLocator();
-// bundle.update(moduleSource.newInputStream(module.getLocator()));
- moduleSource.update(bundle, module);
- OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
- return bundle;
- }
- }
- } catch (Exception e) {
- OsgiBootUtils.error("Could not install module " + module + ": " + e.getMessage(), e);
- }
- return null;
- }
-
- public Collection<Bundle> update() {
- boolean fragmentsUpdated = false;
- Set<Bundle> updatedBundles = new HashSet<>();
- bundles: for (Bundle bundle : bc.getBundles()) {
- for (ProvisioningSource source : sources) {
- String componentId = bundle.getSymbolicName();
- Version version = bundle.getVersion();
- A2Branch branch = source.findBranch(componentId, version);
- if (branch == null)
- continue bundles;
- A2Module module = branch.last();
- Version moduleVersion = module.getVersion();
- int compare = moduleVersion.compareTo(version);
- if (compare > 0) {// update
- try {
- source.update(bundle, module);
-// bundle.update(in);
- String fragmentHost = bundle.getHeaders().get(Constants.FRAGMENT_HOST);
- if (fragmentHost != null)
- fragmentsUpdated = true;
- OsgiBootUtils.info("Updated bundle " + bundle.getLocation() + " to version " + moduleVersion);
- updatedBundles.add(bundle);
- } catch (Exception e) {
- OsgiBootUtils.error("Cannot update with module " + module, e);
- }
- }
- }
- }
- FrameworkWiring frameworkWiring = bc.getBundle(0).adapt(FrameworkWiring.class);
- if (fragmentsUpdated)// refresh all
- frameworkWiring.refreshBundles(null);
- else
- frameworkWiring.refreshBundles(updatedBundles);
- return updatedBundles;
- }
-
- public static void main(String[] args) {
- Map<String, String> configuration = new HashMap<>();
- configuration.put("osgi.console", "2323");
- Framework framework = OsgiBootUtils.launch(new EquinoxFactory(), 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"));
- context.load();
- if (framework.getBundleContext().getBundles().length == 1) {// initial
- pm.install(null);
- } else {
- pm.update();
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- // framework.stop();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot.a2;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Version;
-
-/** Where components are retrieved from. */
-public interface ProvisioningSource {
- /** List all contributions of this source. */
- Iterable<A2Contribution> listContributions(Object filter);
-
- /** Install a module in the OSGi runtime. */
- Bundle install(BundleContext bc, A2Module module);
-
- /** Update a module in the OSGi runtime. */
- void update(Bundle bundle, A2Module module);
-
- /** Finds the {@link A2Branch} related to this component and version. */
- A2Branch findBranch(String componentId, Version version);
-
-}