import static org.argeo.osgi.boot.OsgiBootUtils.warn;
import java.io.File;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
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.boot.internal.springutil.AntPathMatcher;
import org.argeo.osgi.boot.internal.springutil.PathMatcher;
/** Constructor */
public OsgiBoot(BundleContext bundleContext) {
this.bundleContext = bundleContext;
- String homeUri = Paths.get(System.getProperty("user.home")).toUri().toString();
+ Path homePath = Paths.get(System.getProperty("user.home")).toAbsolutePath();
+ String homeUri = homePath.toUri().toString();
localCache = getProperty(PROP_ARGEO_OSGI_LOCAL_CACHE, homeUri + ".m2/repository/");
provisioningManager = new ProvisioningManager(bundleContext);
provisioningManager.registerDefaultSource();
} else {
for (String source : sources.split(",")) {
- provisioningManager.registerSource(source);
+ if (source.trim().equals(A2Source.DEFAULT_A2_URI)) {
+ provisioningManager
+ .registerSource(A2Source.SCHEME_A2 + "://" + homePath.toString() + "/.local/share/osgi");
+ provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/local/share/osgi");
+ provisioningManager.registerSource(A2Source.SCHEME_A2 + ":///usr/share/osgi");
+ } else {
+ provisioningManager.registerSource(source);
+ }
}
}
}
package org.argeo.osgi.boot;
/** OsgiBoot specific exceptions */
-public class OsgiBootException extends RuntimeException {
+class OsgiBootException extends RuntimeException {
private static final long serialVersionUID = 2414011711711425353L;
public OsgiBootException() {
* typically a combination of major and minor version, indicating backward
* compatibility.
*/
-class A2Branch implements Comparable<A2Branch> {
+public class A2Branch implements Comparable<A2Branch> {
private final A2Component component;
private final String id;
final SortedMap<Version, A2Module> modules = Collections.synchronizedSortedMap(new TreeMap<>());
- A2Branch(A2Component component, String id) {
+ public A2Branch(A2Component component, String id) {
this.component = component;
this.id = id;
component.branches.put(id, this);
* <code>Bundle-SymbolicName</code>. This is the equivalent of Maven's artifact
* id.
*/
-class A2Component implements Comparable<A2Component> {
+public class A2Component implements Comparable<A2Component> {
private final A2Contribution contribution;
private final String id;
* 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.
*/
-class A2Contribution implements Comparable<A2Contribution> {
+public class A2Contribution implements Comparable<A2Contribution> {
final static String BOOT = "boot";
final static String RUNTIME = "runtime";
final static String CLASSPATH = "classpath";
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);
+// if (context != null)
+// context.contributions.put(id, this);
}
A2Component getOrAddComponent(String componentId) {
--- /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;
+
+/** 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());
+ }
+ }
+}
* A provisioning source based on the linear classpath with which the JCM has
* been started.
*/
-public class ClasspathSource extends ProvisioningSource {
+public class ClasspathSource extends AbstractProvisioningSource {
void load() throws IOException {
- A2Contribution classpathContribution = new A2Contribution(this, A2Contribution.CLASSPATH);
+ 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);
import org.argeo.osgi.boot.OsgiBootUtils;
import org.osgi.framework.Version;
-/** A file system {@link ProvisioningSource} in A2 format. */
-public class FsA2Source extends ProvisioningSource {
+/** A file system {@link AbstractProvisioningSource} in A2 format. */
+public class FsA2Source extends AbstractProvisioningSource implements A2Source {
private final Path base;
public FsA2Source(Path base) {
String contributionId = contributionPath.getFileName().toString();
if (A2Contribution.BOOT.equals(contributionId))// skip boot
continue contributions;
- A2Contribution contribution = new A2Contribution(this, contributionId);
+ A2Contribution contribution = getOrAddContribution(contributionId);
contributions.add(contribution);
}
}
import org.argeo.osgi.boot.OsgiBootUtils;
import org.osgi.framework.Version;
-/** A file system {@link ProvisioningSource} in Maven 2 format. */
-public class FsM2Source extends ProvisioningSource {
+/** A file system {@link AbstractProvisioningSource} in Maven 2 format. */
+public class FsM2Source extends AbstractProvisioningSource {
private final Path base;
public FsM2Source(Path base) {
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.Version;
-/** A running OSGi bundle context seen as a {@link ProvisioningSource}. */
-class OsgiContext extends ProvisioningSource {
+/** A running OSGi bundle context seen as a {@link AbstractProvisioningSource}. */
+class OsgiContext extends AbstractProvisioningSource {
private final BundleContext bc;
public OsgiContext(BundleContext bc) {
}
void load() {
- A2Contribution runtimeContribution = new A2Contribution(this, A2Contribution.RUNTIME);
+ A2Contribution runtimeContribution = getOrAddContribution( A2Contribution.RUNTIME);
for (Bundle bundle : bc.getBundles()) {
// OsgiBootUtils.debug(bundle.getDataFile("/"));
String componentId = bundle.getSymbolicName();
import java.util.Map;
import java.util.Set;
-import org.argeo.osgi.boot.OsgiBootException;
import org.argeo.osgi.boot.OsgiBootUtils;
import org.eclipse.osgi.launch.EquinoxFactory;
import org.osgi.framework.Bundle;
osgiContext.load();
}
- void addSource(ProvisioningSource context) {
- sources.add(context);
+ protected void addSource(ProvisioningSource source) {
+ sources.add(source);
}
- void installWholeSource(ProvisioningSource context) {
+ void installWholeSource(ProvisioningSource source) {
Set<Bundle> updatedBundles = new HashSet<>();
- for (A2Contribution contribution : context.contributions.values()) {
+ for (A2Contribution contribution : source.listContributions(null)) {
for (A2Component component : contribution.components.values()) {
A2Module module = component.last().last();
Bundle bundle = installOrUpdate(module);
public void registerSource(String uri) {
try {
URI u = new URI(uri);
- if ("a2".equals(u.getScheme())) {
+ if (A2Source.SCHEME_A2.equals(u.getScheme())) {
if (u.getHost() == null || "".equals(u.getHost())) {
String baseStr = u.getPath();
if (File.separatorChar == '\\') {// MS Windows
}
}
} catch (Exception e) {
- throw new OsgiBootException("Cannot add source " + uri, e);
+ throw new A2Exception("Cannot add source " + uri, e);
}
}
String baseStr = base.toString();
if (File.separatorChar == '\\')// MS Windows
baseStr = '/' + baseStr.replace(File.separatorChar, '/');
- URI baseUri = new URI("a2", null, null, 0, baseStr, null, null);
+ 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;
}
}
- /** @return the new/updated bundle, or null if nothign was done. */
- Bundle installOrUpdate(A2Module module) {
+ /** @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();
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.argeo.osgi.boot.OsgiBootException;
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. */
-abstract class ProvisioningSource {
- final Map<String, A2Contribution> contributions = Collections.synchronizedSortedMap(new TreeMap<>());
-
- A2Contribution getOrAddContribution(String contributionId) {
- if (contributions.containsKey(contributionId))
- return contributions.get(contributionId);
- else
- return new A2Contribution(this, contributionId);
- }
-
- 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);
- }
- }
-
- void asTree() {
- StringBuffer buf = new StringBuffer();
- asTree("", buf);
- System.out.println(buf);
- }
-
- 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());
-
- }
-
- 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 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 OsgiBootException("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 OsgiBootException("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 OsgiBootException("Cannot read manifest from " + manifestPath, e);
- }
- } else {
- Path parentPath = currentPath.getParent();
- if (parentPath == null)
- throw new OsgiBootException("MANIFEST.MF file not found.");
- return findManifest(currentPath.getParent());
- }
- }
-
- public Bundle install(BundleContext bc, A2Module module) throws IOException, BundleException {
- 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;
- }
+public interface ProvisioningSource {
+ /** List all contributions of this source. */
+ Iterable<A2Contribution> listContributions(Object filter);
- public void update(Bundle bundle, A2Module module) throws IOException, BundleException {
- 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);
- }
+ /** Install a module in the OSGi runtime. */
+ Bundle install(BundleContext bc, A2Module module);
- 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 OsgiBootException("Cannot install OSGi bundle from " + dir, e);
- }
+ /** 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);
- 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());
- }
- }
}