-argeo.osgi.baseUrl=http://forge.argeo.org/data/java/argeo-2.1/
-argeo.osgi.distributionUrl=org/argeo/commons/org.argeo.dep.cms.sdk/2.1.65/org.argeo.dep.cms.sdk-2.1.65.jar
+#argeo.osgi.baseUrl=http://forge.argeo.org/data/java/argeo-2.1/
+#argeo.osgi.distributionUrl=org/argeo/commons/org.argeo.dep.cms.sdk/2.1.65/org.argeo.dep.cms.sdk-2.1.65.jar
#argeo.osgi.distributionUrl=org/argeo/commons/org.argeo.dep.cms.sdk/2.1.67/org.argeo.dep.cms.sdk-2.1.67.jar
#argeo.osgi.distributionUrl=org/argeo/commons/org.argeo.dep.cms.sdk/2.1.68-SNAPSHOT/org.argeo.dep.cms.sdk-2.1.68-SNAPSHOT.jar
-argeo.osgi.boot.debug=true
+#argeo.osgi.boot.debug=true
argeo.osgi.start.1.osgiboot=org.argeo.osgi.boot
-argeo.osgi.start.2.node=org.eclipse.equinox.http.servlet,org.eclipse.equinox.http.jetty,org.eclipse.equinox.cm,org.eclipse.rap.rwt.osgi
-argeo.osgi.start.3.node=org.argeo.cms,org.eclipse.gemini.blueprint.extender,org.eclipse.equinox.http.registry
+#argeo.osgi.start.2.node=org.eclipse.equinox.http.servlet,org.eclipse.equinox.http.jetty,org.eclipse.equinox.cm,org.eclipse.rap.rwt.osgi
+#argeo.osgi.start.3.node=org.argeo.cms,org.eclipse.gemini.blueprint.extender,org.eclipse.equinox.http.registry
-java.security.manager=
-java.security.policy=file:../../all.policy
+#java.security.manager=
+#java.security.policy=file:../../all.policy
argeo.node.repo.type=localfs
org.osgi.service.http.port=7070
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+ <attributes>
+ <attribute name="module" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.argeo.init</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+/MANIFEST.MF
--- /dev/null
+Main-Class: org.argeo.init.Service
+Class-Path: org.eclipse.osgi.jar
+
+Bundle-Activator: org.argeo.init.osgi.Activator
+
+Import-Package: org.osgi.*;version=0.0.0
+Private-Package: *
+Export-Package: !*
--- /dev/null
+source.. = src/,\
+ ext/test/
+additional.bundles = org.junit,\
+ org.hamcrest
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.argeo.commons</groupId>
+ <version>2.3-SNAPSHOT</version>
+ <artifactId>argeo-commons</artifactId>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>org.argeo.init</artifactId>
+ <packaging>jar</packaging>
+ <name>Init</name>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>true</skipTests>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+<!-- <dependency> -->
+<!-- <groupId>org.argeo.tp</groupId> -->
+<!-- <artifactId>argeo-tp</artifactId> -->
+<!-- <version>${version.argeo-tp}</version> -->
+<!-- <scope>provided</scope> -->
+<!-- </dependency> -->
+
+<!-- <dependency> -->
+<!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
+<!-- <artifactId>org.eclipse.osgi</artifactId> -->
+<!-- <scope>provided</scope> -->
+<!-- </dependency> -->
+
+ <!-- TEST -->
+<!-- <dependency> -->
+<!-- <groupId>org.argeo.tp</groupId> -->
+<!-- <artifactId>junit</artifactId> -->
+<!-- <scope>test</scope> -->
+<!-- </dependency> -->
+ </dependencies>
+
+
+</project>
\ No newline at end of file
--- /dev/null
+org.argeo.init.logging.ThinLoggerFinder
\ No newline at end of file
--- /dev/null
+package org.argeo.init;
+
+public interface RuntimeContext extends Runnable, AutoCloseable {
+ void waitForStop(long timeout) throws InterruptedException;
+}
--- /dev/null
+package org.argeo.init;
+
+import java.lang.System.Logger;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.argeo.init.osgi.OsgiRuntimeContext;
+
+public class Service implements Runnable, AutoCloseable {
+ private final static Logger log = System.getLogger(Service.class.getName());
+
+ private static RuntimeContext runtimeContext = null;
+
+ protected Service(String[] args) {
+ }
+
+ @Override
+ public void run() {
+ }
+
+ @Override
+ public void close() throws Exception {
+ }
+
+ public static void main(String[] args) {
+ long pid = ProcessHandle.current().pid();
+ log.log(Logger.Level.DEBUG, "Starting with PID " + pid);
+
+ // shutdown on exit
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ if (Service.runtimeContext != null)
+ Service.runtimeContext.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }, "Runtime shutdown"));
+
+ Map<String, String> config = new HashMap<>();
+ try {
+ try (OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext(config)) {
+ osgiRuntimeContext.run();
+ Service.runtimeContext = osgiRuntimeContext;
+ Service.runtimeContext.waitForStop(0);
+ } catch (NoClassDefFoundError e) {
+ try (StaticRuntimeContext staticRuntimeContext = new StaticRuntimeContext(config)) {
+ staticRuntimeContext.run();
+ Service.runtimeContext = staticRuntimeContext;
+ Service.runtimeContext.waitForStop(0);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+}
--- /dev/null
+package org.argeo.init;
+
+import java.util.Map;
+
+public class StaticRuntimeContext implements RuntimeContext {
+ private Map<String, String> config;
+
+ private boolean running = false;
+
+ protected StaticRuntimeContext(Map<String, String> config) {
+ this.config = config;
+ }
+
+ @Override
+ public synchronized void run() {
+ running = true;
+ notifyAll();
+ }
+
+ @Override
+ public void waitForStop(long timeout) throws InterruptedException {
+ long begin = System.currentTimeMillis();
+ while (running && (timeout == 0 || System.currentTimeMillis() - begin < timeout)) {
+ synchronized (this) {
+ wait(500);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void close() throws Exception {
+ running = false;
+ }
+
+}
--- /dev/null
+package org.argeo.init.a2;
+
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.argeo.init.osgi.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.init.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.init.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.init.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.init.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.init.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.init.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.init.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.init.osgi.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.init.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.init.osgi.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.init.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.init.osgi.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.init.a2;
+
+import org.argeo.init.osgi.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.init.a2;
+
+import java.io.File;
+import java.net.URI;
+import java.nio.file.Files;
+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.init.osgi.OsgiBootUtils;
+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);
+ if (Files.exists(base)) {
+ FsA2Source source = new FsA2Source(base);
+ source.load();
+ addSource(source);
+ OsgiBootUtils.info("Registered " + uri + " as 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.debug("Default source from framework location " + frameworkLocation);
+ 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(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.init.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);
+
+}
--- /dev/null
+/** A2 OSGi repository format. */
+package org.argeo.init.a2;
\ No newline at end of file
--- /dev/null
+package org.argeo.init.logging;
+
+import java.io.Serializable;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+
+/** A log entry with equals semantics based on an incremental long sequence. */
+class ThinLogEntry implements Serializable {
+ private static final long serialVersionUID = 5915553445193937270L;
+
+ private final static AtomicLong next = new AtomicLong(0l);
+
+// private final transient Logger logger;
+
+ private final long sequence;
+ private final String loggerName;
+ private final Instant instant;
+ private final Level level;
+ private final String message;
+ private final Optional<Throwable> throwable;
+ private final Optional<StackTraceElement> callLocation;
+
+ protected ThinLogEntry(Logger logger, Level level, String message, Instant instant, Throwable e,
+ StackTraceElement callLocation) {
+ // NOTE: 0 is never allocated, in order to have a concept of "null" entry
+ sequence = next.incrementAndGet();
+// this.logger = logger;
+
+ this.loggerName = Objects.requireNonNull(logger).getName();
+ this.instant = Objects.requireNonNull(instant);
+ this.level = level;
+ this.message = message;
+ this.throwable = Optional.ofNullable(e);
+ this.callLocation = Optional.ofNullable(callLocation);
+ }
+
+ public long getSequence() {
+ return sequence;
+ }
+
+ public Level getLevel() {
+ return level;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+// Logger getLogger() {
+// return logger;
+// }
+
+ public String getLoggerName() {
+ return loggerName;
+ }
+
+ public Instant getInstant() {
+ return instant;
+ }
+
+ public Optional<Throwable> getThrowable() {
+ return throwable;
+ }
+
+ public Optional<StackTraceElement> getCallLocation() {
+ return callLocation;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) sequence;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ThinLogEntry))
+ return false;
+ return sequence == ((ThinLogEntry) obj).sequence;
+ }
+
+ @Override
+ public String toString() {
+ return message;
+ }
+
+}
--- /dev/null
+package org.argeo.init.logging;
+
+import java.lang.System.Logger;
+import java.lang.System.LoggerFinder;
+
+/** Factory for Java system logging. */
+public class ThinLoggerFinder extends LoggerFinder {
+ private ThinLogging logging;
+
+ public ThinLoggerFinder() {
+ logging = new ThinLogging();
+ }
+
+ @Override
+ public Logger getLogger(String name, Module module) {
+ return logging.getLogger(name, module);
+ }
+
+}
--- /dev/null
+package org.argeo.init.logging;
+
+import java.io.PrintStream;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscription;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.concurrent.TimeUnit;
+
+/** A thin logging system based on the {@link Logger} framework. */
+class ThinLogging {
+// private static ThinLogging instance;
+
+ private SortedMap<String, ThinLogger> loggers = new TreeMap<>();
+ private NavigableMap<String, Level> levels = new TreeMap<>();
+
+ private final ExecutorService executor;
+ private final LogEntryPublisher publisher;
+
+ ThinLogging() {
+// if (instance != null)
+// throw new IllegalStateException("Only one logger finder cann be instantiated");
+// instance = this;
+
+ executor = Executors.newCachedThreadPool((r) -> {
+ Thread t = new Thread(r);
+ t.setDaemon(true);
+ return t;
+ });
+ publisher = new LogEntryPublisher(executor, Flow.defaultBufferSize());
+
+ PrintStreamSubscriber subscriber = new PrintStreamSubscriber();
+ publisher.subscribe(subscriber);
+
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> close(), "Log shutdown"));
+ }
+
+ protected void close() {
+ publisher.close();
+ try {
+ // we ait a bit in order to make sure all messages are flushed
+ // TODO synchronize more efficiently
+ executor.awaitTermination(300, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ }
+
+ public void setDefaultLevel(Level level) {
+ levels.put("", level);
+ }
+
+ public boolean isLoggable(String name, Level level) {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(level);
+
+ Map.Entry<String, Level> entry = levels.ceilingEntry(name);
+ assert entry != null;
+ return level.getSeverity() >= entry.getValue().getSeverity();
+ }
+
+ public Logger getLogger(String name, Module module) {
+ if (!loggers.containsKey(name)) {
+ ThinLogger logger = new ThinLogger(name);
+ loggers.put(name, logger);
+ }
+ return loggers.get(name);
+ }
+
+ class ThinLogger implements System.Logger {
+ private final String name;
+ private boolean callLocationEnabled = true;
+
+ protected ThinLogger(String name) {
+ assert Objects.nonNull(name);
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return ThinLogging.this.isLoggable(name, level);
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
+ // measure timestamp first
+ Instant now = Instant.now();
+ publisher.log(this, level, bundle, msg, thrown, now, findCallLocation());
+ }
+
+ @Override
+ public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+ // measure timestamp first
+ Instant now = Instant.now();
+ String msg = MessageFormat.format(format, params);
+ publisher.log(this, level, bundle, msg, null, now, findCallLocation());
+ }
+
+ protected StackTraceElement findCallLocation() {
+ StackTraceElement callLocation = null;
+ if (callLocationEnabled) {
+// Throwable locator = new Throwable();
+// StackTraceElement[] stack = locator.getStackTrace();
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+ // TODO make it smarter by finding the lowest logger interface in the stack
+ int lowestLoggerInterface = 0;
+ stack: for (int i = 2; i < stack.length; i++) {
+ String className = stack[i].getClassName();
+ switch (className) {
+ case "java.lang.System$Logger":
+ case "org.apache.commons.logging.Log":
+ case "org.osgi.service.log.Logger":
+ case "org.argeo.cms.Log":
+ lowestLoggerInterface = i;
+ continue stack;
+ default:
+ }
+ }
+ if (stack.length > lowestLoggerInterface + 1)
+ callLocation = stack[lowestLoggerInterface + 1];
+ }
+ return callLocation;
+ }
+
+ }
+
+ class LogEntryPublisher extends SubmissionPublisher<ThinLogEntry> {
+
+ protected LogEntryPublisher(Executor executor, int maxBufferCapacity) {
+ super(executor, maxBufferCapacity);
+ }
+
+ void log(ThinLogger logger, Level level, ResourceBundle bundle, String msg, Throwable thrown, Instant instant,
+ StackTraceElement callLocation) {
+ ThinLogEntry logEntry = new ThinLogEntry(logger, level, msg, instant, thrown, callLocation);
+ submit(logEntry);
+ }
+
+ }
+
+ class PrintStreamSubscriber implements Flow.Subscriber<ThinLogEntry> {
+ private PrintStream out;
+ private PrintStream err;
+ private int writeToErrLevel = Level.WARNING.getSeverity();
+
+ private boolean journald = false;
+
+ protected PrintStreamSubscriber() {
+ this(System.out, System.err);
+ }
+
+ protected PrintStreamSubscriber(PrintStream out, PrintStream err) {
+ this.out = out;
+ this.err = err;
+ }
+
+ @Override
+ public void onSubscribe(Subscription subscription) {
+ subscription.request(Long.MAX_VALUE);
+ }
+
+ @Override
+ public void onNext(ThinLogEntry item) {
+ if (item.getLevel().getSeverity() >= writeToErrLevel) {
+ err.print(toPrint(item));
+ } else {
+ out.print(toPrint(item));
+ }
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ throwable.printStackTrace(err);
+ }
+
+ @Override
+ public void onComplete() {
+ out.flush();
+ err.flush();
+ }
+
+ protected boolean prefixOnEachLine() {
+ return journald;
+ }
+
+ protected String firstLinePrefix(ThinLogEntry logEntry) {
+ return journald ? linePrefix(logEntry)
+ : logEntry.getLevel().toString() + "\t" + logEntry.getInstant() + " ";
+ }
+
+ protected String firstLineSuffix(ThinLogEntry logEntry) {
+ return " - " + (logEntry.getCallLocation().isEmpty() ? logEntry.getLoggerName()
+ : logEntry.getCallLocation().get());
+ }
+
+ protected String linePrefix(ThinLogEntry logEntry) {
+ return journald ? "<" + levelToJournald(logEntry.getLevel()) + ">" : "";
+ }
+
+ protected int levelToJournald(Level level) {
+ int severity = level.getSeverity();
+ if (severity >= Level.ERROR.getSeverity())
+ return 3;
+ else if (severity >= Level.WARNING.getSeverity())
+ return 4;
+ else if (severity >= Level.INFO.getSeverity())
+ return 6;
+ else
+ return 7;
+ }
+
+ protected String toPrint(ThinLogEntry logEntry) {
+ StringBuilder sb = new StringBuilder();
+ StringTokenizer st = new StringTokenizer(logEntry.getMessage(), "\r\n");
+ assert st.hasMoreTokens();
+
+ // first line
+ String firstLine = st.nextToken();
+ sb.append(firstLinePrefix(logEntry));
+ sb.append(firstLine);
+ sb.append(firstLineSuffix(logEntry));
+ sb.append('\n');
+
+ // other lines
+ String prefix = linePrefix(logEntry);
+ while (st.hasMoreTokens()) {
+ sb.append(prefix);
+ sb.append(st.nextToken());
+ sb.append('\n');
+ }
+
+ if (!logEntry.getThrowable().isEmpty()) {
+ Throwable throwable = logEntry.getThrowable().get();
+ sb.append(prefix);
+ addThrowable(sb, prefix, throwable);
+ }
+ return sb.toString();
+ }
+
+ protected void addThrowable(StringBuilder sb, String prefix, Throwable throwable) {
+ sb.append(throwable.getClass().getName());
+ sb.append(": ");
+ sb.append(throwable.getMessage());
+ sb.append('\n');
+ for (StackTraceElement ste : throwable.getStackTrace()) {
+ sb.append(prefix);
+ sb.append(ste.toString());
+ sb.append('\n');
+ }
+ if (throwable.getCause() != null) {
+ sb.append(prefix);
+ sb.append("caused by ");
+ addThrowable(sb, prefix, throwable.getCause());
+ }
+ }
+ }
+
+ public static void main(String args[]) {
+ Logger logger = System.getLogger(ThinLogging.class.getName());
+ logger.log(Logger.Level.INFO, "Hello log!");
+ logger.log(Logger.Level.ERROR, "Hello error!");
+ logger.log(Logger.Level.DEBUG, "Hello multi\nline\ndebug!");
+ logger.log(Logger.Level.WARNING, "Hello exception!", new Throwable());
+ }
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+import java.util.Vector;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * An OSGi configurator. See
+ * <a href="http://wiki.eclipse.org/Configurator">http:
+ * //wiki.eclipse.org/Configurator</a>
+ */
+public class Activator implements BundleActivator {
+ private Long checkpoint = null;
+
+ public void start(final BundleContext bundleContext) throws Exception {
+ // admin thread
+ Thread adminThread = new AdminThread(bundleContext);
+ adminThread.start();
+
+ // bootstrap
+ OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+ if (checkpoint == null) {
+ osgiBoot.bootstrap();
+ checkpoint = System.currentTimeMillis();
+ } else {
+ osgiBoot.update();
+ checkpoint = System.currentTimeMillis();
+ }
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ }
+
+ class JournaldResourceBundle extends ResourceBundle {
+
+ @Override
+ protected Object handleGetObject(String key) {
+ switch (key) {
+ case "ERROR":
+ return "<5>";
+ }
+ return null;
+ }
+
+ @Override
+ public Enumeration<String> getKeys() {
+ Vector<String> keys = new Vector<>();
+ keys.add("ERROR");
+ return keys.elements();
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.io.File;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.launch.Framework;
+
+/** Monitors the runtime and can shut it down. */
+public class AdminThread extends Thread {
+ public final static String PROP_ARGEO_OSGI_SHUTDOWN_FILE = "argeo.osgi.shutdownFile";
+ private File shutdownFile;
+ private final BundleContext bundleContext;
+
+ public AdminThread(BundleContext bundleContext) {
+ super("OSGi Boot Admin");
+ this.bundleContext = bundleContext;
+ if (System.getProperty(PROP_ARGEO_OSGI_SHUTDOWN_FILE) != null) {
+ shutdownFile = new File(
+ System.getProperty(PROP_ARGEO_OSGI_SHUTDOWN_FILE));
+ if (!shutdownFile.exists()) {
+ shutdownFile = null;
+ OsgiBootUtils.warn("Shutdown file " + shutdownFile
+ + " not found, feature deactivated");
+ }
+ }
+ }
+
+ public void run() {
+ if (shutdownFile != null) {
+ // wait for file to be removed
+ while (shutdownFile.exists()) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ Framework framework = (Framework) bundleContext.getBundle(0);
+ try {
+ // shutdown framework
+ framework.stop();
+ // wait 10 mins for shutdown
+ framework.waitForStop(10 * 60 * 1000);
+ // close VM
+ System.exit(0);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+ }
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/** Intermediary structure used by path matching */
+class BundlesSet {
+ private String baseUrl = "reference:file";// not used yet
+ private final String dir;
+ private List<String> includes = new ArrayList<String>();
+ private List<String> excludes = new ArrayList<String>();
+
+ public BundlesSet(String def) {
+ StringTokenizer st = new StringTokenizer(def, ";");
+
+ if (!st.hasMoreTokens())
+ throw new RuntimeException("Base dir not defined.");
+ try {
+ String dirPath = st.nextToken();
+
+ if (dirPath.startsWith("file:"))
+ dirPath = dirPath.substring("file:".length());
+
+ dir = new File(dirPath.replace('/', File.separatorChar)).getCanonicalPath();
+ if (OsgiBootUtils.debug)
+ OsgiBootUtils.debug("Base dir: " + dir);
+ } catch (IOException e) {
+ throw new RuntimeException("Cannot convert to absolute path", e);
+ }
+
+ while (st.hasMoreTokens()) {
+ String tk = st.nextToken();
+ StringTokenizer stEq = new StringTokenizer(tk, "=");
+ String type = stEq.nextToken();
+ String pattern = stEq.nextToken();
+ if ("in".equals(type) || "include".equals(type)) {
+ includes.add(pattern);
+ } else if ("ex".equals(type) || "exclude".equals(type)) {
+ excludes.add(pattern);
+ } else if ("baseUrl".equals(type)) {
+ baseUrl = pattern;
+ } else {
+ System.err.println("Unkown bundles pattern type " + type);
+ }
+ }
+
+ // if (excludeSvn && !excludes.contains(EXCLUDES_SVN_PATTERN)) {
+ // excludes.add(EXCLUDES_SVN_PATTERN);
+ // }
+ }
+
+ public String getDir() {
+ return dir;
+ }
+
+ public List<String> getIncludes() {
+ return includes;
+ }
+
+ public List<String> getExcludes() {
+ return excludes;
+ }
+
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+/**
+ * A distribution bundle is a bundle within a maven-like distribution
+ * groupId:Bundle-SymbolicName:Bundle-Version which references others OSGi
+ * bundle. It is not required to be OSGi complete also it will generally be
+ * expected that it is. The root of the repository is computed based on the file
+ * name of the URL and of the content of the index.
+ */
+public class DistributionBundle {
+ private final static String INDEX_FILE_NAME = "modularDistribution.csv";
+
+ private final String url;
+
+ private Manifest manifest;
+ private String symbolicName;
+ private String version;
+
+ /** can be null */
+ private String baseUrl;
+ /** can be null */
+ private String relativeUrl;
+ private String localCache;
+
+ private List<OsgiArtifact> artifacts;
+
+ private String separator = ",";
+
+ public DistributionBundle(String url) {
+ this.url = url;
+ }
+
+ public DistributionBundle(String baseUrl, String relativeUrl, String localCache) {
+ if (baseUrl == null || !baseUrl.endsWith("/"))
+ throw new IllegalArgumentException("Base url " + baseUrl + " badly formatted");
+ if (relativeUrl.startsWith("http") || relativeUrl.startsWith("file:"))
+ throw new IllegalArgumentException("Relative URL " + relativeUrl + " badly formatted");
+ this.url = constructUrl(baseUrl, relativeUrl);
+ this.baseUrl = baseUrl;
+ this.relativeUrl = relativeUrl;
+ this.localCache = localCache;
+ }
+
+ protected String constructUrl(String baseUrl, String relativeUrl) {
+ try {
+ if (relativeUrl.indexOf('*') >= 0) {
+ if (!baseUrl.startsWith("file:"))
+ throw new IllegalArgumentException(
+ "Wildcard support only for file:, badly formatted " + baseUrl + " and " + relativeUrl);
+ Path basePath = Paths.get(new URI(baseUrl));
+ // Path basePath = Paths.get(new URI(baseUrl));
+ // int li = relativeUrl.lastIndexOf('/');
+ // String relativeDir = relativeUrl.substring(0, li);
+ // String relativeFile = relativeUrl.substring(li,
+ // relativeUrl.length());
+ String pattern = "glob:" + basePath + '/' + relativeUrl;
+ PathMatcher pm = basePath.getFileSystem().getPathMatcher(pattern);
+ SortedMap<Version, Path> res = new TreeMap<>();
+ checkDir(basePath, pm, res);
+ if (res.size() == 0)
+ throw new IllegalArgumentException("No file matching " + relativeUrl + " found in " + baseUrl);
+ return res.get(res.firstKey()).toUri().toString();
+ } else {
+ return baseUrl + relativeUrl;
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot build URL from " + baseUrl + " and " + relativeUrl, e);
+ }
+ }
+
+ private void checkDir(Path dir, PathMatcher pm, SortedMap<Version, Path> res) throws IOException {
+ try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
+ for (Path path : ds) {
+ if (Files.isDirectory(path))
+ checkDir(path, pm, res);
+ else if (pm.matches(path)) {
+ String fileName = path.getFileName().toString();
+ fileName = fileName.substring(0, fileName.lastIndexOf('.'));
+ if (fileName.endsWith("-SNAPSHOT"))
+ fileName = fileName.substring(0, fileName.lastIndexOf('-')) + ".SNAPSHOT";
+ fileName = fileName.substring(fileName.lastIndexOf('-') + 1);
+ Version version = new Version(fileName);
+ res.put(version, path);
+ }
+ }
+ }
+ }
+
+ public void processUrl() {
+ JarInputStream jarIn = null;
+ try {
+ URL u = new URL(url);
+
+ // local cache
+ URI localUri = new URI(localCache + relativeUrl);
+ Path localPath = Paths.get(localUri);
+ if (Files.exists(localPath))
+ u = localUri.toURL();
+ jarIn = new JarInputStream(u.openStream());
+
+ // meta data
+ manifest = jarIn.getManifest();
+ symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+
+ JarEntry indexEntry;
+ while ((indexEntry = jarIn.getNextJarEntry()) != null) {
+ String entryName = indexEntry.getName();
+ if (entryName.equals(INDEX_FILE_NAME)) {
+ break;
+ }
+ jarIn.closeEntry();
+ }
+
+ // list artifacts
+ if (indexEntry == null)
+ throw new IllegalArgumentException("No index " + INDEX_FILE_NAME + " in " + url);
+ artifacts = listArtifacts(jarIn);
+ jarIn.closeEntry();
+
+ // find base URL
+ // won't work if distribution artifact is not listed
+ for (int i = 0; i < artifacts.size(); i++) {
+ OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
+ if (osgiArtifact.getSymbolicName().equals(symbolicName) && osgiArtifact.getVersion().equals(version)) {
+ String relativeUrl = osgiArtifact.getRelativeUrl();
+ if (url.endsWith(relativeUrl)) {
+ baseUrl = url.substring(0, url.length() - osgiArtifact.getRelativeUrl().length());
+ break;
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot list URLs from " + url, e);
+ } finally {
+ if (jarIn != null)
+ try {
+ jarIn.close();
+ } catch (IOException e) {
+ // silent
+ }
+ }
+ }
+
+ protected List<OsgiArtifact> listArtifacts(InputStream in) {
+ List<OsgiArtifact> osgiArtifacts = new ArrayList<OsgiArtifact>();
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new InputStreamReader(in));
+ String line = null;
+ lines: while ((line = reader.readLine()) != null) {
+ StringTokenizer st = new StringTokenizer(line, separator);
+ String moduleName = st.nextToken();
+ String moduleVersion = st.nextToken();
+ String relativeUrl = st.nextToken();
+ if (relativeUrl.endsWith(".pom"))
+ continue lines;
+ osgiArtifacts.add(new OsgiArtifact(moduleName, moduleVersion, relativeUrl));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot list artifacts", e);
+ }
+ return osgiArtifacts;
+ }
+
+ /** Convenience method */
+ public static DistributionBundle processUrl(String baseUrl, String relativeUrl, String localCache) {
+ DistributionBundle distributionBundle = new DistributionBundle(baseUrl, relativeUrl, localCache);
+ distributionBundle.processUrl();
+ return distributionBundle;
+ }
+
+ /**
+ * List full URLs of the bundles, based on base URL, usable directly for
+ * download.
+ */
+ public List<String> listUrls() {
+ if (baseUrl == null)
+ throw new IllegalArgumentException("Base URL is not set");
+
+ if (artifacts == null)
+ throw new IllegalStateException("Artifact list not initialized");
+
+ List<String> urls = new ArrayList<String>();
+ for (int i = 0; i < artifacts.size(); i++) {
+ OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
+ // local cache
+ URI localUri;
+ try {
+ localUri = new URI(localCache + relativeUrl);
+ } catch (URISyntaxException e) {
+ OsgiBootUtils.warn(e.getMessage());
+ localUri = null;
+ }
+ Version version = new Version(osgiArtifact.getVersion());
+ if (localUri != null && Files.exists(Paths.get(localUri)) && version.getQualifier() != null
+ && version.getQualifier().startsWith("SNAPSHOT")) {
+ urls.add(localCache + osgiArtifact.getRelativeUrl());
+ } else {
+ urls.add(baseUrl + osgiArtifact.getRelativeUrl());
+ }
+ }
+ return urls;
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ /** Separator used to parse the tabular file */
+ public void setSeparator(String modulesUrlSeparator) {
+ this.separator = modulesUrlSeparator;
+ }
+
+ public String getRelativeUrl() {
+ return relativeUrl;
+ }
+
+ /** One of the listed artifact */
+ protected static class OsgiArtifact {
+ private final String symbolicName;
+ private final String version;
+ private final String relativeUrl;
+
+ public OsgiArtifact(String symbolicName, String version, String relativeUrl) {
+ super();
+ this.symbolicName = symbolicName;
+ this.version = version;
+ this.relativeUrl = relativeUrl;
+ }
+
+ public String getSymbolicName() {
+ return symbolicName;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public String getRelativeUrl() {
+ return relativeUrl;
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+
+/** An OSGi launcher executing first another class in the system class path. */
+public class Launcher {
+
+ public static void main(String[] args) {
+ // Try to load system properties
+ String systemPropertiesFilePath = getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE);
+ if (systemPropertiesFilePath != null) {
+ FileInputStream in;
+ try {
+ in = new FileInputStream(systemPropertiesFilePath);
+ System.getProperties().load(in);
+ } catch (IOException e1) {
+ throw new RuntimeException("Cannot load system properties from " + systemPropertiesFilePath, e1);
+ }
+ if (in != null) {
+ try {
+ in.close();
+ } catch (Exception e) {
+ // silent
+ }
+ }
+ }
+
+ // Start main class
+ startMainClass();
+
+ // Start Equinox
+ BundleContext bundleContext = null;
+ try {
+ bundleContext = OsgiBootUtils.launch(OsgiBootUtils.equinoxArgsToConfiguration(args)).getBundleContext();
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot start Equinox.", e);
+ }
+
+ // OSGi bootstrap
+ OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+ osgiBoot.bootstrap();
+ }
+
+ protected static void startMainClass() {
+ String className = getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS);
+ if (className == null)
+ return;
+
+ String line = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPARGS, "");
+
+ String[] uiArgs = readArgumentsFromLine(line);
+
+ try {
+ // Launch main method using reflection
+ Class<?> clss = Class.forName(className);
+ Class<?>[] mainArgsClasses = new Class[] { uiArgs.getClass() };
+ Object[] mainArgs = { uiArgs };
+ Method mainMethod = clss.getMethod("main", mainArgsClasses);
+ mainMethod.invoke(null, mainArgs);
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot start main class.", e);
+ }
+
+ }
+
+ /**
+ * Transform a line into an array of arguments, taking "" as single arguments.
+ * (nested \" are not supported)
+ */
+ private static String[] readArgumentsFromLine(String lineOrig) {
+ String line = lineOrig.trim();// remove trailing spaces
+ List<String> args = new ArrayList<String>();
+ StringBuffer curr = new StringBuffer("");
+ boolean inQuote = false;
+ char[] arr = line.toCharArray();
+ for (int i = 0; i < arr.length; i++) {
+ char c = arr[i];
+ switch (c) {
+ case '\"':
+ inQuote = !inQuote;
+ break;
+ case ' ':
+ if (!inQuote) {// otherwise, no break: goes to default
+ if (curr.length() > 0) {
+ args.add(curr.toString());
+ curr = new StringBuffer("");
+ }
+ break;
+ }
+ default:
+ curr.append(c);
+ break;
+ }
+ }
+
+ // Add last arg
+ if (curr.length() > 0) {
+ args.add(curr.toString());
+ curr = null;
+ }
+
+ String[] res = new String[args.size()];
+ for (int i = 0; i < args.size(); i++) {
+ res[i] = args.get(i).toString();
+ }
+ return res;
+ }
+
+ public static String getProperty(String name, String defaultValue) {
+ final String value;
+ if (defaultValue != null)
+ value = System.getProperty(name, defaultValue);
+ else
+ value = System.getProperty(name);
+
+ if (value == null || value.equals(""))
+ return null;
+ else
+ return value;
+ }
+
+ public static String getProperty(String name) {
+ return getProperty(name, null);
+ }
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.lang.management.ManagementFactory;
+
+public class Main {
+
+ public static void main(String[] args) {
+ String mainClass = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS);
+ if (mainClass == null) {
+ throw new IllegalArgumentException(
+ "System property " + OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS + " must be specified");
+ }
+
+ OsgiBuilder osgi = new OsgiBuilder();
+ String distributionUrl = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_DISTRIBUTION_URL);
+ if (distributionUrl != null)
+ osgi.install(distributionUrl);
+ // osgi.conf("argeo.node.useradmin.uris", "os:///");
+ // osgi.conf("osgi.clean", "true");
+ // osgi.conf("osgi.console", "true");
+ osgi.launch();
+
+ if (OsgiBootUtils.isDebug()) {
+ long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
+ String jvmUptimeStr = (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s";
+ OsgiBootUtils.debug("Ready to launch " + mainClass + " in " + jvmUptimeStr);
+ }
+
+ osgi.main(mainClass, args);
+
+ osgi.shutdown();
+
+ }
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ServiceLoader;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+
+/** Launch an OSGi framework and deploy a CMS Node into it. */
+public class NodeRunner {
+ private Long timeout = 30 * 1000l;
+ private final Path baseDir;
+ private final Path confDir;
+ private final Path dataDir;
+
+ private String baseUrl = "http://forge.argeo.org/data/java/argeo-2.1/";
+ private String distributionUrl = null;
+
+ private Framework framework = null;
+
+ public NodeRunner(String distributionUrl, Path baseDir) {
+ this.distributionUrl = distributionUrl;
+ Path mavenBase = Paths.get(System.getProperty("user.home") + "/.m2/repository");
+ Path osgiBase = Paths.get("/user/share/osgi");
+ if (Files.exists(mavenBase)) {
+ Path mavenPath = mavenBase.resolve(distributionUrl);
+ if (Files.exists(mavenPath))
+ baseUrl = mavenBase.toUri().toString();
+ } else if (Files.exists(osgiBase)) {
+ Path osgiPath = osgiBase.resolve(distributionUrl);
+ if (Files.exists(osgiPath))
+ baseUrl = osgiBase.toUri().toString();
+ }
+
+ this.baseDir = baseDir;
+ this.confDir = this.baseDir.resolve("state");
+ this.dataDir = this.baseDir.resolve("data");
+
+ }
+
+ public void start() {
+ long begin = System.currentTimeMillis();
+ // log4j
+ Path log4jFile = confDir.resolve("log4j.properties");
+ if (!Files.exists(log4jFile))
+ copyResource("/org/argeo/osgi/boot/log4j.properties", log4jFile);
+ System.setProperty("log4j.configuration", "file://" + log4jFile.toAbsolutePath());
+
+ // Start Equinox
+ try {
+ ServiceLoader<FrameworkFactory> ff = ServiceLoader.load(FrameworkFactory.class);
+ FrameworkFactory frameworkFactory = ff.iterator().next();
+ Map<String, String> configuration = new HashMap<String, String>();
+ configuration.put("osgi.configuration.area", confDir.toAbsolutePath().toString());
+ configuration.put("osgi.instance.area", dataDir.toAbsolutePath().toString());
+ defaultConfiguration(configuration);
+
+ framework = frameworkFactory.newFramework(configuration);
+ framework.start();
+ info("## Date : " + new Date());
+ info("## Data : " + dataDir.toAbsolutePath());
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot start OSGi framework", e);
+ }
+ BundleContext bundleContext = framework.getBundleContext();
+ try {
+
+ // Spring configs currently require System properties
+ // System.getProperties().putAll(configuration);
+
+ // expected by JAAS as System.property FIXME
+ System.setProperty("osgi.instance.area", bundleContext.getProperty("osgi.instance.area"));
+
+ // OSGi bootstrap
+ OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
+
+ osgiBoot.installUrls(osgiBoot.getDistributionUrls(distributionUrl, baseUrl));
+
+ // Start runtime
+ Properties startProperties = new Properties();
+ // TODO make it possible to override it
+ startProperties.put("argeo.osgi.start.2.node",
+ "org.eclipse.equinox.http.servlet,org.eclipse.equinox.http.jetty,"
+ + "org.eclipse.equinox.metatype,org.eclipse.equinox.cm,org.eclipse.rap.rwt.osgi");
+ startProperties.put("argeo.osgi.start.3.node", "org.argeo.cms");
+ startProperties.put("argeo.osgi.start.4.node",
+ "org.eclipse.gemini.blueprint.extender,org.eclipse.equinox.http.registry");
+ osgiBoot.startBundles(startProperties);
+
+ // Find node repository
+ ServiceReference<?> sr = null;
+ while (sr == null) {
+ sr = bundleContext.getServiceReference("javax.jcr.Repository");
+ if (System.currentTimeMillis() - begin > timeout)
+ throw new RuntimeException("Could find node after " + timeout + "ms");
+ Thread.sleep(100);
+ }
+ Object nodeDeployment = bundleContext.getService(sr);
+ info("Node Deployment " + nodeDeployment);
+
+ // Initialization completed
+ long duration = System.currentTimeMillis() - begin;
+ info("## CMS Launcher initialized in " + (duration / 1000) + "s " + (duration % 1000) + "ms");
+ } catch (Exception e) {
+ shutdown();
+ throw new RuntimeException("Cannot start CMS", e);
+ } finally {
+
+ }
+ }
+
+ private void defaultConfiguration(Map<String, String> configuration) {
+ // all permissions to OSGi security manager
+ Path policyFile = confDir.resolve("node.policy");
+ if (!Files.exists(policyFile))
+ copyResource("/org/argeo/osgi/boot/node.policy", policyFile);
+ configuration.put("java.security.policy", "file://" + policyFile.toAbsolutePath());
+
+ configuration.put("org.eclipse.rap.workbenchAutostart", "false");
+ configuration.put("org.eclipse.equinox.http.jetty.autostart", "false");
+ 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");
+
+ // Do clean
+ // configuration.put("osgi.clean", "true");
+ // if (args.length == 0) {
+ // configuration.put("osgi.console", "");
+ // }
+ }
+
+ public void shutdown() {
+ try {
+ framework.stop();
+ framework.waitForStop(15 * 1000);
+ } catch (Exception silent) {
+ }
+ }
+
+ public Path getConfDir() {
+ return confDir;
+ }
+
+ public Path getDataDir() {
+ return dataDir;
+ }
+
+ public Framework getFramework() {
+ return framework;
+ }
+
+ public static void main(String[] args) {
+ try {
+ String distributionUrl;
+ Path executionDir;
+ if (args.length == 2) {
+ distributionUrl = args[0];
+ executionDir = Paths.get(args[1]);
+ } else if (args.length == 1) {
+ executionDir = Paths.get(System.getProperty("user.dir"));
+ distributionUrl = args[0];
+ } else if (args.length == 0) {
+ executionDir = Paths.get(System.getProperty("user.dir"));
+ distributionUrl = "org/argeo/commons/org.argeo.dep.cms.sdk/2.1.70/org.argeo.dep.cms.sdk-2.1.70.jar";
+ }else{
+ printUsage();
+ System.exit(1);
+ return;
+ }
+
+ NodeRunner nodeRunner = new NodeRunner(distributionUrl, executionDir);
+ nodeRunner.start();
+// if (args.length != 0)
+// System.exit(0);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ protected static void info(Object msg) {
+ System.out.println(msg);
+ }
+
+ protected static void err(Object msg) {
+ System.err.println(msg);
+ }
+
+ protected static void debug(Object msg) {
+ System.out.println(msg);
+ }
+
+ protected static void copyResource(String resource, Path targetFile) {
+ InputStream input = null;
+ OutputStream output = null;
+ try {
+ input = NodeRunner.class.getResourceAsStream(resource);
+ Files.createDirectories(targetFile.getParent());
+ output = Files.newOutputStream(targetFile);
+ byte[] buf = new byte[8192];
+ while (true) {
+ int length = input.read(buf);
+ if (length < 0)
+ break;
+ output.write(buf, 0, length);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot write " + resource + " file to " + targetFile, e);
+ } finally {
+ try {
+ input.close();
+ } catch (Exception ignore) {
+ }
+ try {
+ output.close();
+ } catch (Exception ignore) {
+ }
+ }
+
+ }
+
+ static void printUsage(){
+ err("Usage: <distribution url> <base dir>");
+ }
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import static org.argeo.init.osgi.OsgiBootUtils.debug;
+import static org.argeo.init.osgi.OsgiBootUtils.warn;
+
+import java.io.File;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+import org.argeo.init.a2.A2Source;
+import org.argeo.init.a2.ProvisioningManager;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.Version;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+/**
+ * Basic provisioning of an OSGi runtime via file path patterns and system
+ * properties. The approach is to generate list of URLs based on various
+ * methods, configured via properties.
+ */
+public class OsgiBoot implements OsgiBootConstants {
+ public final static String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
+ public final static String PROP_ARGEO_OSGI_SOURCES = "argeo.osgi.sources";
+
+ public final static String PROP_ARGEO_OSGI_BUNDLES = "argeo.osgi.bundles";
+ public final static String PROP_ARGEO_OSGI_BASE_URL = "argeo.osgi.baseUrl";
+ public final static String PROP_ARGEO_OSGI_LOCAL_CACHE = "argeo.osgi.localCache";
+ public final static String PROP_ARGEO_OSGI_DISTRIBUTION_URL = "argeo.osgi.distributionUrl";
+
+ // booleans
+ public final static String PROP_ARGEO_OSGI_BOOT_DEBUG = "argeo.osgi.boot.debug";
+ // public final static String PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN =
+ // "argeo.osgi.boot.excludeSvn";
+
+ public final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE = "argeo.osgi.boot.systemPropertiesFile";
+ public final static String PROP_ARGEO_OSGI_BOOT_APPCLASS = "argeo.osgi.boot.appclass";
+ public final static String PROP_ARGEO_OSGI_BOOT_APPARGS = "argeo.osgi.boot.appargs";
+
+ public final static String DEFAULT_BASE_URL = "reference:file:";
+ // public final static String EXCLUDES_SVN_PATTERN = "**/.svn/**";
+
+ // OSGi system properties
+ final static String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL = "osgi.bundles.defaultStartLevel";
+ final static String PROP_OSGI_STARTLEVEL = "osgi.startLevel";
+ final static String INSTANCE_AREA_PROP = "osgi.instance.area";
+ final static String CONFIGURATION_AREA_PROP = "osgi.configuration.area";
+
+ // Symbolic names
+ public final static String SYMBOLIC_NAME_OSGI_BOOT = "org.argeo.osgi.boot";
+ public final static String SYMBOLIC_NAME_EQUINOX = "org.eclipse.osgi";
+
+ /** Exclude svn metadata implicitely(a bit costly) */
+ // private boolean excludeSvn =
+ // Boolean.valueOf(System.getProperty(PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN,
+ // "false"))
+ // .booleanValue();
+
+ /** Default is 10s */
+ @Deprecated
+ private long defaultTimeout = 10000l;
+
+ private final BundleContext bundleContext;
+ private final String localCache;
+
+ private final ProvisioningManager provisioningManager;
+
+ /*
+ * INITIALIZATION
+ */
+ /** Constructor */
+ public OsgiBoot(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ 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);
+ String sources = getProperty(PROP_ARGEO_OSGI_SOURCES);
+ if (sources == null) {
+ provisioningManager.registerDefaultSource();
+ } else {
+ for (String source : sources.split(",")) {
+ if (source.trim().equals(A2Source.DEFAULT_A2_URI)) {
+ if (Files.exists(homePath))
+ 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);
+ }
+ }
+ }
+ }
+
+ ProvisioningManager getProvisioningManager() {
+ return provisioningManager;
+ }
+
+ /*
+ * HIGH-LEVEL METHODS
+ */
+ /** Bootstraps the OSGi runtime */
+ public void bootstrap() {
+ try {
+ long begin = System.currentTimeMillis();
+ System.out.println();
+ String osgiInstancePath = bundleContext.getProperty(INSTANCE_AREA_PROP);
+ OsgiBootUtils
+ .info("OSGi bootstrap starting" + (osgiInstancePath != null ? " (" + osgiInstancePath + ")" : ""));
+ installUrls(getBundlesUrls());
+ installUrls(getDistributionUrls());
+ provisioningManager.install(null);
+ startBundles();
+ long duration = System.currentTimeMillis() - begin;
+ OsgiBootUtils.info("OSGi bootstrap completed in " + Math.round(((double) duration) / 1000) + "s ("
+ + duration + "ms), " + bundleContext.getBundles().length + " bundles");
+ } catch (RuntimeException e) {
+ OsgiBootUtils.error("OSGi bootstrap FAILED", e);
+ throw e;
+ }
+
+ // diagnostics
+ if (OsgiBootUtils.debug) {
+ OsgiBootDiagnostics diagnostics = new OsgiBootDiagnostics(bundleContext);
+ diagnostics.checkUnresolved();
+ Map<String, Set<String>> duplicatePackages = diagnostics.findPackagesExportedTwice();
+ if (duplicatePackages.size() > 0) {
+ OsgiBootUtils.info("Packages exported twice:");
+ Iterator<String> it = duplicatePackages.keySet().iterator();
+ while (it.hasNext()) {
+ String pkgName = it.next();
+ OsgiBootUtils.info(pkgName);
+ Set<String> bdles = duplicatePackages.get(pkgName);
+ Iterator<String> bdlesIt = bdles.iterator();
+ while (bdlesIt.hasNext())
+ OsgiBootUtils.info(" " + bdlesIt.next());
+ }
+ }
+ }
+ System.out.println();
+ }
+
+ public void update() {
+ provisioningManager.update();
+ }
+
+ /*
+ * INSTALLATION
+ */
+ /** Install a single url. Convenience method. */
+ public Bundle installUrl(String url) {
+ List<String> urls = new ArrayList<String>();
+ urls.add(url);
+ installUrls(urls);
+ return (Bundle) getBundlesByLocation().get(url);
+ }
+
+ /** Install the bundles at this URL list. */
+ public void installUrls(List<String> urls) {
+ Map<String, Bundle> installedBundles = getBundlesByLocation();
+ for (int i = 0; i < urls.size(); i++) {
+ String url = (String) urls.get(i);
+ installUrl(url, installedBundles);
+ }
+ refreshFramework();
+ }
+
+ /** Actually install the provided URL */
+ protected void installUrl(String url, Map<String, Bundle> installedBundles) {
+ try {
+ if (installedBundles.containsKey(url)) {
+ Bundle bundle = (Bundle) installedBundles.get(url);
+ if (OsgiBootUtils.debug)
+ debug("Bundle " + bundle.getSymbolicName() + " already installed from " + url);
+ } else if (url.contains("/" + SYMBOLIC_NAME_EQUINOX + "/")
+ || url.contains("/" + SYMBOLIC_NAME_OSGI_BOOT + "/")) {
+ if (OsgiBootUtils.debug)
+ warn("Skip " + url);
+ return;
+ } else {
+ Bundle bundle = bundleContext.installBundle(url);
+ if (url.startsWith("http"))
+ OsgiBootUtils
+ .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
+ else if (OsgiBootUtils.debug)
+ OsgiBootUtils.debug(
+ "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
+ assert bundle.getSymbolicName() != null;
+ // uninstall previous versions
+ bundles: for (Bundle b : bundleContext.getBundles()) {
+ if (b.getSymbolicName() == null)
+ continue bundles;
+ if (bundle.getSymbolicName().equals(b.getSymbolicName())) {
+ Version bundleV = bundle.getVersion();
+ Version bV = b.getVersion();
+ if (bV == null)
+ continue bundles;
+ if (bundleV.getMajor() == bV.getMajor() && bundleV.getMinor() == bV.getMinor()) {
+ if (bundleV.getMicro() > bV.getMicro()) {
+ // uninstall older bundles
+ b.uninstall();
+ OsgiBootUtils.debug("Uninstalled " + b);
+ } else if (bundleV.getMicro() < bV.getMicro()) {
+ // uninstall just installed bundle if newer
+ bundle.uninstall();
+ OsgiBootUtils.debug("Uninstalled " + bundle);
+ break bundles;
+ } else {
+ // uninstall any other with same major/minor
+ if (!bundleV.getQualifier().equals(bV.getQualifier())) {
+ b.uninstall();
+ OsgiBootUtils.debug("Uninstalled " + b);
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (BundleException e) {
+ final String ALREADY_INSTALLED = "is already installed";
+ String message = e.getMessage();
+ if ((message.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT + "\"")
+ || message.contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX + "\""))
+ && message.contains(ALREADY_INSTALLED)) {
+ // silent, in order to avoid warnings: we know that both
+ // have already been installed...
+ } else {
+ if (message.contains(ALREADY_INSTALLED)) {
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.warn("Duplicate install from " + url + ": " + message);
+ } else
+ OsgiBootUtils.warn("Could not install bundle from " + url + ": " + message);
+ }
+ if (OsgiBootUtils.debug && !message.contains(ALREADY_INSTALLED))
+ e.printStackTrace();
+ }
+ }
+
+ /*
+ * START
+ */
+ public void startBundles() {
+ startBundles(System.getProperties());
+ }
+
+ public void startBundles(Properties properties) {
+ FrameworkStartLevel frameworkStartLevel = bundleContext.getBundle(0).adapt(FrameworkStartLevel.class);
+
+ // default and active start levels from System properties
+ Integer defaultStartLevel = Integer.parseInt(getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4"));
+ Integer activeStartLevel = Integer.parseInt(getProperty(PROP_OSGI_STARTLEVEL, "6"));
+
+ SortedMap<Integer, List<String>> startLevels = new TreeMap<Integer, List<String>>();
+ computeStartLevels(startLevels, properties, defaultStartLevel);
+ // inverts the map for the time being, TODO optimise
+ Map<String, Integer> bundleStartLevels = new HashMap<>();
+ for (Integer level : startLevels.keySet()) {
+ for (String bsn : startLevels.get(level))
+ bundleStartLevels.put(bsn, level);
+ }
+ for (Bundle bundle : bundleContext.getBundles()) {
+ String bsn = bundle.getSymbolicName();
+ if (bundleStartLevels.containsKey(bsn)) {
+ BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class);
+ Integer level = bundleStartLevels.get(bsn);
+ if (bundleStartLevel.getStartLevel() != level || !bundleStartLevel.isPersistentlyStarted()) {
+ bundleStartLevel.setStartLevel(level);
+ try {
+ bundle.start();
+ } catch (BundleException e) {
+ OsgiBootUtils.error("Cannot mark " + bsn + " as started", e);
+ }
+ if (getDebug())
+ OsgiBootUtils.debug(bsn + " starts at level " + level);
+ }
+ }
+ }
+ frameworkStartLevel.setStartLevel(activeStartLevel, (FrameworkEvent event) -> {
+ if (getDebug())
+ OsgiBootUtils.debug("Framework event: " + event);
+ int initialStartLevel = frameworkStartLevel.getInitialBundleStartLevel();
+ int startLevel = frameworkStartLevel.getStartLevel();
+ OsgiBootUtils.debug("Framework start level: " + startLevel + " (initial: " + initialStartLevel + ")");
+ });
+ }
+
+ private static void computeStartLevels(SortedMap<Integer, List<String>> startLevels, Properties properties,
+ Integer defaultStartLevel) {
+
+ // default (and previously, only behaviour)
+ appendToStartLevels(startLevels, defaultStartLevel, properties.getProperty(PROP_ARGEO_OSGI_START, ""));
+
+ // list argeo.osgi.start.* system properties
+ Iterator<Object> keys = properties.keySet().iterator();
+ final String prefix = PROP_ARGEO_OSGI_START + ".";
+ while (keys.hasNext()) {
+ String key = keys.next().toString();
+ if (key.startsWith(prefix)) {
+ Integer startLevel;
+ String suffix = key.substring(prefix.length());
+ String[] tokens = suffix.split("\\.");
+ if (tokens.length > 0 && !tokens[0].trim().equals(""))
+ try {
+ // first token is start level
+ startLevel = Integer.parseInt(tokens[0]);
+ } catch (NumberFormatException e) {
+ startLevel = defaultStartLevel;
+ }
+ else
+ startLevel = defaultStartLevel;
+
+ // append bundle names
+ String bundleNames = properties.getProperty(key);
+ appendToStartLevels(startLevels, startLevel, bundleNames);
+ }
+ }
+ }
+
+ /** Append a comma-separated list of bundles to the start levels. */
+ private static void appendToStartLevels(SortedMap<Integer, List<String>> startLevels, Integer startLevel,
+ String str) {
+ if (str == null || str.trim().equals(""))
+ return;
+
+ if (!startLevels.containsKey(startLevel))
+ startLevels.put(startLevel, new ArrayList<String>());
+ String[] bundleNames = str.split(",");
+ for (int i = 0; i < bundleNames.length; i++) {
+ if (bundleNames[i] != null && !bundleNames[i].trim().equals(""))
+ (startLevels.get(startLevel)).add(bundleNames[i]);
+ }
+ }
+
+ /**
+ * Start the provided list of bundles
+ *
+ * @return whether all bundles are now in active state
+ * @deprecated
+ */
+ @Deprecated
+ public boolean startBundles(List<String> bundlesToStart) {
+ if (bundlesToStart.size() == 0)
+ return true;
+
+ // used to monitor ACTIVE states
+ List<Bundle> startedBundles = new ArrayList<Bundle>();
+ // used to log the bundles not found
+ List<String> notFoundBundles = new ArrayList<String>(bundlesToStart);
+
+ Bundle[] bundles = bundleContext.getBundles();
+ long startBegin = System.currentTimeMillis();
+ for (int i = 0; i < bundles.length; i++) {
+ Bundle bundle = bundles[i];
+ String symbolicName = bundle.getSymbolicName();
+ if (bundlesToStart.contains(symbolicName))
+ try {
+ try {
+ bundle.start();
+ if (OsgiBootUtils.debug)
+ debug("Bundle " + symbolicName + " started");
+ } catch (Exception e) {
+ OsgiBootUtils.warn("Start of bundle " + symbolicName + " failed because of " + e
+ + ", maybe bundle is not yet resolved," + " waiting and trying again.");
+ waitForBundleResolvedOrActive(startBegin, bundle);
+ bundle.start();
+ startedBundles.add(bundle);
+ }
+ notFoundBundles.remove(symbolicName);
+ } catch (Exception e) {
+ OsgiBootUtils.warn("Bundle " + symbolicName + " cannot be started: " + e.getMessage());
+ if (OsgiBootUtils.debug)
+ e.printStackTrace();
+ // was found even if start failed
+ notFoundBundles.remove(symbolicName);
+ }
+ }
+
+ for (int i = 0; i < notFoundBundles.size(); i++)
+ OsgiBootUtils.warn("Bundle '" + notFoundBundles.get(i) + "' not started because it was not found.");
+
+ // monitors that all bundles are started
+ long beginMonitor = System.currentTimeMillis();
+ boolean allStarted = !(startedBundles.size() > 0);
+ List<String> notStarted = new ArrayList<String>();
+ while (!allStarted && (System.currentTimeMillis() - beginMonitor) < defaultTimeout) {
+ notStarted = new ArrayList<String>();
+ allStarted = true;
+ for (int i = 0; i < startedBundles.size(); i++) {
+ Bundle bundle = (Bundle) startedBundles.get(i);
+ // TODO check behaviour of lazs bundles
+ if (bundle.getState() != Bundle.ACTIVE) {
+ allStarted = false;
+ notStarted.add(bundle.getSymbolicName());
+ }
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ }
+ long duration = System.currentTimeMillis() - beginMonitor;
+
+ if (!allStarted)
+ for (int i = 0; i < notStarted.size(); i++)
+ OsgiBootUtils.warn("Bundle '" + notStarted.get(i) + "' not ACTIVE after " + (duration / 1000) + "s");
+
+ return allStarted;
+ }
+
+ /** Waits for a bundle to become active or resolved */
+ @Deprecated
+ private void waitForBundleResolvedOrActive(long startBegin, Bundle bundle) throws Exception {
+ int originalState = bundle.getState();
+ if ((originalState == Bundle.RESOLVED) || (originalState == Bundle.ACTIVE))
+ return;
+
+ String originalStateStr = OsgiBootUtils.stateAsString(originalState);
+
+ int currentState = bundle.getState();
+ while (!(currentState == Bundle.RESOLVED || currentState == Bundle.ACTIVE)) {
+ long now = System.currentTimeMillis();
+ if ((now - startBegin) > defaultTimeout * 10)
+ throw new Exception("Bundle " + bundle.getSymbolicName() + " was not RESOLVED or ACTIVE after "
+ + (now - startBegin) + "ms (originalState=" + originalStateStr + ", currentState="
+ + OsgiBootUtils.stateAsString(currentState) + ")");
+
+ try {
+ Thread.sleep(100l);
+ } catch (InterruptedException e) {
+ // silent
+ }
+ currentState = bundle.getState();
+ }
+ }
+
+ /*
+ * BUNDLE PATTERNS INSTALLATION
+ */
+ /**
+ * Computes a list of URLs based on Ant-like include/exclude patterns defined by
+ * ${argeo.osgi.bundles} with the following format:<br>
+ * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
+ * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
+ * without a new line causes a '.' to be appended with unexpected side effects.
+ */
+ public List<String> getBundlesUrls() {
+ String bundlePatterns = getProperty(PROP_ARGEO_OSGI_BUNDLES);
+ return getBundlesUrls(bundlePatterns);
+ }
+
+ /**
+ * Compute a list of URLs to install based on the provided patterns, with
+ * default base url
+ */
+ public List<String> getBundlesUrls(String bundlePatterns) {
+ String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL, DEFAULT_BASE_URL);
+ return getBundlesUrls(baseUrl, bundlePatterns);
+ }
+
+ /** Implements the path matching logic */
+ public List<String> getBundlesUrls(String baseUrl, String bundlePatterns) {
+ List<String> urls = new ArrayList<String>();
+ if (bundlePatterns == null)
+ return urls;
+
+// bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns);
+ if (OsgiBootUtils.debug)
+ debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns);
+
+ StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
+ List<BundlesSet> bundlesSets = new ArrayList<BundlesSet>();
+ while (st.hasMoreTokens()) {
+ String token = st.nextToken();
+ if (new File(token).exists()) {
+ String url = locationToUrl(baseUrl, token);
+ urls.add(url);
+ } else
+ bundlesSets.add(new BundlesSet(token));
+ }
+
+ // find included
+ List<String> included = new ArrayList<String>();
+// PathMatcher matcher = new AntPathMatcher();
+ for (int i = 0; i < bundlesSets.size(); i++) {
+ BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
+ for (int j = 0; j < bundlesSet.getIncludes().size(); j++) {
+ String pattern = (String) bundlesSet.getIncludes().get(j);
+ match(included, bundlesSet.getDir(), null, pattern);
+ }
+ }
+
+ // find excluded
+ List<String> excluded = new ArrayList<String>();
+ for (int i = 0; i < bundlesSets.size(); i++) {
+ BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
+ for (int j = 0; j < bundlesSet.getExcludes().size(); j++) {
+ String pattern = (String) bundlesSet.getExcludes().get(j);
+ match(excluded, bundlesSet.getDir(), null, pattern);
+ }
+ }
+
+ // construct list
+ for (int i = 0; i < included.size(); i++) {
+ String fullPath = (String) included.get(i);
+ if (!excluded.contains(fullPath))
+ urls.add(locationToUrl(baseUrl, fullPath));
+ }
+
+ return urls;
+ }
+
+ /*
+ * DISTRIBUTION JAR INSTALLATION
+ */
+ public List<String> getDistributionUrls() {
+ String distributionUrl = getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL);
+ String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL);
+ return getDistributionUrls(distributionUrl, baseUrl);
+ }
+
+ public List<String> getDistributionUrls(String distributionUrl, String baseUrl) {
+ List<String> urls = new ArrayList<String>();
+ if (distributionUrl == null)
+ return urls;
+
+ DistributionBundle distributionBundle;
+ if (distributionUrl.startsWith("http") || distributionUrl.startsWith("file")) {
+ distributionBundle = new DistributionBundle(distributionUrl);
+ if (baseUrl != null)
+ distributionBundle.setBaseUrl(baseUrl);
+ } else {
+ // relative url
+ if (baseUrl == null) {
+ baseUrl = localCache;
+ }
+
+ if (distributionUrl.contains(":")) {
+ // TODO make it safer
+ String[] parts = distributionUrl.trim().split(":");
+ String[] categoryParts = parts[0].split("\\.");
+ String artifactId = parts[1];
+ String version = parts[2];
+ StringBuilder sb = new StringBuilder();
+ for (String categoryPart : categoryParts) {
+ sb.append(categoryPart).append('/');
+ }
+ sb.append(artifactId).append('/');
+ sb.append(version).append('/');
+ sb.append(artifactId).append('-').append(version).append(".jar");
+ distributionUrl = sb.toString();
+ }
+
+ distributionBundle = new DistributionBundle(baseUrl, distributionUrl, localCache);
+ }
+ // if (baseUrl != null && !(distributionUrl.startsWith("http") ||
+ // distributionUrl.startsWith("file"))) {
+ // // relative url
+ // distributionBundle = new DistributionBundle(baseUrl, distributionUrl,
+ // localCache);
+ // } else {
+ // distributionBundle = new DistributionBundle(distributionUrl);
+ // if (baseUrl != null)
+ // distributionBundle.setBaseUrl(baseUrl);
+ // }
+ distributionBundle.processUrl();
+ return distributionBundle.listUrls();
+ }
+
+ /*
+ * HIGH LEVEL UTILITIES
+ */
+ /** Actually performs the matching logic. */
+ protected void match(List<String> matched, String base, String currentPath, String pattern) {
+ if (currentPath == null) {
+ // Init
+ File baseDir = new File(base.replace('/', File.separatorChar));
+ File[] files = baseDir.listFiles();
+
+ if (files == null) {
+ if (OsgiBootUtils.debug)
+ OsgiBootUtils.warn("Base dir " + baseDir + " has no children, exists=" + baseDir.exists()
+ + ", isDirectory=" + baseDir.isDirectory());
+ return;
+ }
+
+ for (int i = 0; i < files.length; i++)
+ match(matched, base, files[i].getName(), pattern);
+ } else {
+ PathMatcher matcher = FileSystems.getDefault().getPathMatcher(pattern);
+ String fullPath = base + '/' + currentPath;
+ if (matched.contains(fullPath))
+ return;// don't try deeper if already matched
+
+ boolean ok = matcher.matches(Paths.get(currentPath));
+ // if (debug)
+ // debug(currentPath + " " + (ok ? "" : " not ")
+ // + " matched with " + pattern);
+ if (ok) {
+ matched.add(fullPath);
+ return;
+ } else {
+ String newFullPath = relativeToFullPath(base, currentPath);
+ File newFile = new File(newFullPath);
+ File[] files = newFile.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ String newCurrentPath = currentPath + '/' + files[i].getName();
+ if (files[i].isDirectory()) {
+// if (matcher.matchStart(pattern, newCurrentPath)) {
+ // FIXME recurse only if start matches ?
+ match(matched, base, newCurrentPath, pattern);
+// } else {
+// if (OsgiBootUtils.debug)
+// debug(newCurrentPath + " does not start match with " + pattern);
+//
+// }
+ } else {
+ boolean nonDirectoryOk = matcher.matches(Paths.get(newCurrentPath));
+ if (OsgiBootUtils.debug)
+ debug(currentPath + " " + (ok ? "" : " not ") + " matched with " + pattern);
+ if (nonDirectoryOk)
+ matched.add(relativeToFullPath(base, newCurrentPath));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected void matchFile() {
+
+ }
+
+ /*
+ * LOW LEVEL UTILITIES
+ */
+ /**
+ * The bundles already installed. Key is location (String) , value is a
+ * {@link Bundle}
+ */
+ public Map<String, Bundle> getBundlesByLocation() {
+ Map<String, Bundle> installedBundles = new HashMap<String, Bundle>();
+ Bundle[] bundles = bundleContext.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ installedBundles.put(bundles[i].getLocation(), bundles[i]);
+ }
+ return installedBundles;
+ }
+
+ /**
+ * The bundles already installed. Key is symbolic name (String) , value is a
+ * {@link Bundle}
+ */
+ public Map<String, Bundle> getBundlesBySymbolicName() {
+ Map<String, Bundle> namedBundles = new HashMap<String, Bundle>();
+ Bundle[] bundles = bundleContext.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ namedBundles.put(bundles[i].getSymbolicName(), bundles[i]);
+ }
+ return namedBundles;
+ }
+
+ /** Creates an URL from a location */
+ protected String locationToUrl(String baseUrl, String location) {
+ return baseUrl + location;
+ }
+
+ /** Transforms a relative path in a full system path. */
+ protected String relativeToFullPath(String basePath, String relativePath) {
+ return (basePath + '/' + relativePath).replace('/', File.separatorChar);
+ }
+
+ private void refreshFramework() {
+ Bundle systemBundle = bundleContext.getBundle(0);
+ FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class);
+ frameworkWiring.refreshBundles(null);
+ }
+
+ /**
+ * Gets a property value
+ *
+ * @return null when defaultValue is ""
+ */
+ public String getProperty(String name, String defaultValue) {
+ String value = bundleContext.getProperty(name);
+ if (value == null)
+ return defaultValue; // may be null
+ else
+ return value;
+ }
+
+ public String getProperty(String name) {
+ return getProperty(name, null);
+ }
+
+ /*
+ * BEAN METHODS
+ */
+
+ public boolean getDebug() {
+ return OsgiBootUtils.debug;
+ }
+
+ // public void setDebug(boolean debug) {
+ // this.debug = debug;
+ // }
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public String getLocalCache() {
+ return localCache;
+ }
+
+ // public void setDefaultTimeout(long defaultTimeout) {
+ // this.defaultTimeout = defaultTimeout;
+ // }
+
+ // public boolean isExcludeSvn() {
+ // return excludeSvn;
+ // }
+ //
+ // public void setExcludeSvn(boolean excludeSvn) {
+ // this.excludeSvn = excludeSvn;
+ // }
+
+ /*
+ * INTERNAL CLASSES
+ */
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+public interface OsgiBootConstants {
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+@SuppressWarnings("deprecation")
+class OsgiBootDiagnostics {
+ private final BundleContext bundleContext;
+
+ public OsgiBootDiagnostics(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+ /*
+ * DIAGNOSTICS
+ */
+ /** Check unresolved bundles */
+ protected void checkUnresolved() {
+ // Refresh
+ ServiceReference<PackageAdmin> packageAdminRef = bundleContext.getServiceReference(PackageAdmin.class);
+ PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(packageAdminRef);
+ packageAdmin.resolveBundles(null);
+
+ Bundle[] bundles = bundleContext.getBundles();
+ List<Bundle> unresolvedBundles = new ArrayList<Bundle>();
+ for (int i = 0; i < bundles.length; i++) {
+ int bundleState = bundles[i].getState();
+ if (!(bundleState == Bundle.ACTIVE || bundleState == Bundle.RESOLVED || bundleState == Bundle.STARTING))
+ unresolvedBundles.add(bundles[i]);
+ }
+
+ if (unresolvedBundles.size() != 0) {
+ OsgiBootUtils.warn("Unresolved bundles " + unresolvedBundles);
+ }
+ }
+
+ /** List packages exported twice. */
+ public Map<String, Set<String>> findPackagesExportedTwice() {
+ ServiceReference<PackageAdmin> paSr = bundleContext.getServiceReference(PackageAdmin.class);
+ PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(paSr);
+
+ // find packages exported twice
+ Bundle[] bundles = bundleContext.getBundles();
+ Map<String, Set<String>> exportedPackages = new TreeMap<String, Set<String>>();
+ for (int i = 0; i < bundles.length; i++) {
+ Bundle bundle = bundles[i];
+ ExportedPackage[] pkgs = packageAdmin.getExportedPackages(bundle);
+ if (pkgs != null)
+ for (int j = 0; j < pkgs.length; j++) {
+ String pkgName = pkgs[j].getName();
+ if (!exportedPackages.containsKey(pkgName)) {
+ exportedPackages.put(pkgName, new TreeSet<String>());
+ }
+ (exportedPackages.get(pkgName)).add(bundle.getSymbolicName() + "_" + bundle.getVersion());
+ }
+ }
+ Map<String, Set<String>> duplicatePackages = new TreeMap<String, Set<String>>();
+ Iterator<String> it = exportedPackages.keySet().iterator();
+ while (it.hasNext()) {
+ String pkgName = it.next().toString();
+ Set<String> bdles = exportedPackages.get(pkgName);
+ if (bdles.size() > 1)
+ duplicatePackages.put(pkgName, bdles);
+ }
+ return duplicatePackages;
+ }
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.StringTokenizer;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+
+/** Utilities, mostly related to logging. */
+public class OsgiBootUtils {
+ /** ISO8601 (as per log4j) and difference to UTC */
+ private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS Z");
+
+ static boolean debug = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_DEBUG) == null ? false
+ : !System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_DEBUG).trim().equals("false");
+
+ public static void info(Object obj) {
+ System.out.println("# OSGiBOOT # " + dateFormat.format(new Date()) + " # " + obj);
+ }
+
+ public static void debug(Object obj) {
+ if (debug)
+ System.out.println("# OSGiBOOT DBG # " + dateFormat.format(new Date()) + " # " + obj);
+ }
+
+ public static void warn(Object obj) {
+ System.out.println("# OSGiBOOT WARN # " + dateFormat.format(new Date()) + " # " + obj);
+ }
+
+ public static void error(Object obj, Throwable e) {
+ System.err.println("# OSGiBOOT ERR # " + dateFormat.format(new Date()) + " # " + obj);
+ if (e != null)
+ e.printStackTrace();
+ }
+
+ public static boolean isDebug() {
+ return debug;
+ }
+
+ public static String stateAsString(int state) {
+ switch (state) {
+ case Bundle.UNINSTALLED:
+ return "UNINSTALLED";
+ case Bundle.INSTALLED:
+ return "INSTALLED";
+ case Bundle.RESOLVED:
+ return "RESOLVED";
+ case Bundle.STARTING:
+ return "STARTING";
+ case Bundle.ACTIVE:
+ return "ACTIVE";
+ case Bundle.STOPPING:
+ return "STOPPING";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+ /**
+ * @return ==0: versions are identical, <0: tested version is newer, >0:
+ * currentVersion is newer.
+ */
+ public static int compareVersions(String currentVersion, String testedVersion) {
+ List<String> cToks = new ArrayList<String>();
+ StringTokenizer cSt = new StringTokenizer(currentVersion, ".");
+ while (cSt.hasMoreTokens())
+ cToks.add(cSt.nextToken());
+ List<String> tToks = new ArrayList<String>();
+ StringTokenizer tSt = new StringTokenizer(currentVersion, ".");
+ while (tSt.hasMoreTokens())
+ tToks.add(tSt.nextToken());
+
+ int comp = 0;
+ comp: for (int i = 0; i < cToks.size(); i++) {
+ if (tToks.size() <= i) {
+ // equals until then, tested shorter
+ comp = 1;
+ break comp;
+ }
+
+ String c = (String) cToks.get(i);
+ String t = (String) tToks.get(i);
+
+ try {
+ int cInt = Integer.parseInt(c);
+ int tInt = Integer.parseInt(t);
+ if (cInt == tInt)
+ continue comp;
+ else {
+ comp = (cInt - tInt);
+ break comp;
+ }
+ } catch (NumberFormatException e) {
+ if (c.equals(t))
+ continue comp;
+ else {
+ comp = c.compareTo(t);
+ break comp;
+ }
+ }
+ }
+
+ if (comp == 0 && tToks.size() > cToks.size()) {
+ // equals until then, current shorter
+ comp = -1;
+ }
+
+ return comp;
+ }
+
+ public static Framework launch(Map<String, String> configuration) {
+ Optional<FrameworkFactory> frameworkFactory = ServiceLoader.load(FrameworkFactory.class).findFirst();
+ if (frameworkFactory.isEmpty())
+ throw new IllegalStateException("No framework factory found");
+ return launch(frameworkFactory.get(), configuration);
+ }
+
+ /** Launch an OSGi framework. */
+ public static Framework launch(FrameworkFactory frameworkFactory, Map<String, String> configuration) {
+ // start OSGi
+ Framework framework = frameworkFactory.newFramework(configuration);
+ try {
+ framework.start();
+ } catch (BundleException e) {
+ throw new IllegalStateException("Cannot start OSGi framework", e);
+ }
+ return framework;
+ }
+
+ public static Map<String, String> equinoxArgsToConfiguration(String[] args) {
+ // FIXME implement it
+ return new HashMap<>();
+ }
+
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.launch.Framework;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.ServiceTracker;
+
+/** OSGi builder, focusing on ease of use for scripting. */
+public class OsgiBuilder {
+ private final static String PROP_HTTP_PORT = "org.osgi.service.http.port";
+ private final static String PROP_HTTPS_PORT = "org.osgi.service.https.port";
+ private final static String PROP_OSGI_CLEAN = "osgi.clean";
+
+ private Map<Integer, StartLevel> startLevels = new TreeMap<>();
+ private List<String> distributionBundles = new ArrayList<>();
+
+ private Map<String, String> configuration = new HashMap<String, String>();
+ private Framework framework;
+ private String baseUrl = null;
+
+ public OsgiBuilder() {
+ // configuration.put("osgi.clean", "true");
+ configuration.put(OsgiBoot.CONFIGURATION_AREA_PROP, System.getProperty(OsgiBoot.CONFIGURATION_AREA_PROP));
+ configuration.put(OsgiBoot.INSTANCE_AREA_PROP, System.getProperty(OsgiBoot.INSTANCE_AREA_PROP));
+ configuration.put(PROP_OSGI_CLEAN, System.getProperty(PROP_OSGI_CLEAN));
+ }
+
+ public Framework launch() {
+ // start OSGi
+ framework = OsgiBootUtils.launch(configuration);
+
+ BundleContext bc = framework.getBundleContext();
+ String osgiData = bc.getProperty(OsgiBoot.INSTANCE_AREA_PROP);
+ // String osgiConf = bc.getProperty(OsgiBoot.CONFIGURATION_AREA_PROP);
+ String osgiConf = framework.getDataFile("").getAbsolutePath();
+ if (OsgiBootUtils.isDebug())
+ OsgiBootUtils.debug("OSGi starting - data: " + osgiData + " conf: " + osgiConf);
+
+ OsgiBoot osgiBoot = new OsgiBoot(framework.getBundleContext());
+ if (distributionBundles.isEmpty()) {
+ osgiBoot.getProvisioningManager().install(null);
+ } else {
+ // install bundles
+ for (String distributionBundle : distributionBundles) {
+ List<String> bundleUrls = osgiBoot.getDistributionUrls(distributionBundle, baseUrl);
+ osgiBoot.installUrls(bundleUrls);
+ }
+ }
+ // start bundles
+ osgiBoot.startBundles(startLevelsToProperties());
+
+ // if (OsgiBootUtils.isDebug())
+ // for (Bundle bundle : bc.getBundles()) {
+ // OsgiBootUtils.debug(bundle.getLocation());
+ // }
+ return framework;
+ }
+
+ public OsgiBuilder conf(String key, String value) {
+ checkNotLaunched();
+ configuration.put(key, value);
+ return this;
+ }
+
+ public OsgiBuilder install(String uri) {
+ // TODO dynamic install
+ checkNotLaunched();
+ if (!distributionBundles.contains(uri))
+ distributionBundles.add(uri);
+ return this;
+ }
+
+ public OsgiBuilder start(int startLevel, String bundle) {
+ // TODO dynamic start
+ checkNotLaunched();
+ StartLevel sl;
+ if (!startLevels.containsKey(startLevel))
+ startLevels.put(startLevel, new StartLevel());
+ sl = startLevels.get(startLevel);
+ sl.add(bundle);
+ return this;
+ }
+
+ public OsgiBuilder waitForServlet(String base) {
+ service("(&(objectClass=javax.servlet.Servlet)(osgi.http.whiteboard.servlet.pattern=" + base + "))");
+ return this;
+ }
+
+ public OsgiBuilder waitForBundle(String bundles) {
+ List<String> lst = new ArrayList<>();
+ Collections.addAll(lst, bundles.split(","));
+ BundleTracker<Object> bt = new BundleTracker<Object>(getBc(), Bundle.ACTIVE, null) {
+
+ @Override
+ public Object addingBundle(Bundle bundle, BundleEvent event) {
+ if (lst.contains(bundle.getSymbolicName())) {
+ return bundle.getSymbolicName();
+ } else {
+ return null;
+ }
+ }
+ };
+ bt.open();
+ while (bt.getTrackingCount() != lst.size()) {
+ try {
+ Thread.sleep(500l);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ bt.close();
+ return this;
+
+ }
+
+ public OsgiBuilder main(String clssUri, String[] args) {
+
+ // waitForBundle(bundleSymbolicName);
+ try {
+ URI uri = new URI(clssUri);
+ if (!"bundleclass".equals(uri.getScheme()))
+ throw new IllegalArgumentException("Unsupported scheme for " + clssUri);
+ String bundleSymbolicName = uri.getHost();
+ String clss = uri.getPath().substring(1);
+ Bundle bundle = null;
+ for (Bundle b : getBc().getBundles()) {
+ if (bundleSymbolicName.equals(b.getSymbolicName())) {
+ bundle = b;
+ break;
+ }
+ }
+ if (bundle == null)
+ throw new IllegalStateException("Bundle " + bundleSymbolicName + " not found");
+ Class<?> c = bundle.loadClass(clss);
+ Object[] mainArgs = { args };
+ Method mainMethod = c.getMethod("main", String[].class);
+ mainMethod.invoke(null, mainArgs);
+ } catch (Throwable e) {
+ throw new RuntimeException("Cannot execute " + clssUri, e);
+ }
+ return this;
+ }
+
+ public Object service(String service) {
+ return service(service, 0);
+ }
+
+ public Object service(String service, long timeout) {
+ ServiceTracker<Object, Object> st;
+ if (service.contains("(")) {
+ try {
+ st = new ServiceTracker<>(getBc(), FrameworkUtil.createFilter(service), null);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException("Badly formatted filter", e);
+ }
+ } else {
+ st = new ServiceTracker<>(getBc(), service, null);
+ }
+ st.open();
+ try {
+ return st.waitForService(timeout);
+ } catch (InterruptedException e) {
+ OsgiBootUtils.error("Interrupted", e);
+ return null;
+ } finally {
+ st.close();
+ }
+
+ }
+
+ public void shutdown() {
+ checkLaunched();
+ try {
+ framework.stop();
+ } catch (BundleException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ try {
+ framework.waitForStop(10 * 60 * 1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+
+ public void setHttpPort(Integer port) {
+ checkNotLaunched();
+ configuration.put(PROP_HTTP_PORT, Integer.toString(port));
+ }
+
+ public void setHttpsPort(Integer port) {
+ checkNotLaunched();
+ configuration.put(PROP_HTTPS_PORT, Integer.toString(port));
+ }
+
+ public void setClean(boolean clean) {
+ checkNotLaunched();
+ configuration.put(PROP_OSGI_CLEAN, Boolean.toString(clean));
+ }
+
+ public Integer getHttpPort() {
+ if (!isLaunched()) {
+ if (configuration.containsKey(PROP_HTTP_PORT))
+ return Integer.parseInt(configuration.get(PROP_HTTP_PORT));
+ else
+ return -1;
+ } else {
+ // TODO wait for service?
+ ServiceReference<?> sr = getBc().getServiceReference("org.osgi.service.http.HttpService");
+ if (sr == null)
+ return -1;
+ Object port = sr.getProperty("http.port");
+ if (port == null)
+ return -1;
+ return Integer.parseInt(port.toString());
+ }
+ }
+
+ public Integer getHttpsPort() {
+ if (!isLaunched()) {
+ if (configuration.containsKey(PROP_HTTPS_PORT))
+ return Integer.parseInt(configuration.get(PROP_HTTPS_PORT));
+ else
+ return -1;
+ } else {
+ // TODO wait for service?
+ ServiceReference<?> sr = getBc().getServiceReference("org.osgi.service.http.HttpService");
+ if (sr == null)
+ return -1;
+ Object port = sr.getProperty("https.port");
+ if (port == null)
+ return -1;
+ return Integer.parseInt(port.toString());
+ }
+ }
+
+ public Object spring(String bundle) {
+ return service("(&(Bundle-SymbolicName=" + bundle + ")"
+ + "(objectClass=org.springframework.context.ApplicationContext))");
+ }
+
+ //
+ // BEAN
+ //
+
+ public BundleContext getBc() {
+ checkLaunched();
+ return framework.getBundleContext();
+ }
+
+ public void setBaseUrl(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ //
+ // UTILITIES
+ //
+ private Properties startLevelsToProperties() {
+ Properties properties = new Properties();
+ for (Integer startLevel : startLevels.keySet()) {
+ String property = OsgiBoot.PROP_ARGEO_OSGI_START + "." + startLevel;
+ StringBuilder value = new StringBuilder();
+ for (String bundle : startLevels.get(startLevel).getBundles()) {
+ value.append(bundle);
+ value.append(',');
+ }
+ // TODO remove trailing comma
+ properties.put(property, value.toString());
+ }
+ return properties;
+ }
+
+ private void checkLaunched() {
+ if (!isLaunched())
+ throw new IllegalStateException("OSGi runtime is not launched");
+ }
+
+ private void checkNotLaunched() {
+ if (isLaunched())
+ throw new IllegalStateException("OSGi runtime already launched");
+ }
+
+ private boolean isLaunched() {
+ return framework != null;
+ }
+
+ private static class StartLevel {
+ private Set<String> bundles = new HashSet<>();
+
+ public void add(String bundle) {
+ String[] b = bundle.split(",");
+ Collections.addAll(bundles, b);
+ }
+
+ public Set<String> getBundles() {
+ return bundles;
+ }
+ }
+}
--- /dev/null
+package org.argeo.init.osgi;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.ServiceLoader;
+
+import org.argeo.init.RuntimeContext;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+
+public class OsgiRuntimeContext implements RuntimeContext {
+ private Map<String, String> config;
+ private Framework framework;
+ private OsgiBoot osgiBoot;
+
+ public OsgiRuntimeContext(Map<String, String> config) {
+ this.config = config;
+ }
+
+ @Override
+ public void run() {
+ ServiceLoader<FrameworkFactory> sl = ServiceLoader.load(FrameworkFactory.class);
+ Optional<FrameworkFactory> opt = sl.findFirst();
+ if (opt.isEmpty())
+ throw new IllegalStateException("Cannot find OSGi framework");
+ framework = opt.get().newFramework(config);
+ try {
+ framework.start();
+ BundleContext bundleContext = framework.getBundleContext();
+ osgiBoot = new OsgiBoot(bundleContext);
+ osgiBoot.bootstrap();
+ } catch (BundleException e) {
+ throw new IllegalStateException("Cannot start OSGi framework", e);
+ }
+ }
+
+ @Override
+ public void waitForStop(long timeout) throws InterruptedException {
+ if (framework == null)
+ throw new IllegalStateException("Framework is not initialised");
+ framework.waitForStop(timeout);
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (framework != null)
+ framework.stop();
+ }
+
+}
--- /dev/null
+log4j.rootLogger=WARN, console
+
+log4j.logger.org.argeo=INFO
+
+## Appenders
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c - [%t]%n
+
+log4j.appender.development=org.apache.log4j.ConsoleAppender
+log4j.appender.development.layout=org.apache.log4j.PatternLayout
+log4j.appender.development.layout.ConversionPattern=%d{ABSOLUTE} %m (%F:%L) [%t] %p %n
--- /dev/null
+grant {
+ permission java.security.AllPermission;
+};
\ No newline at end of file
--- /dev/null
+/** Simple OSGi initialisation. */
+package org.argeo.init.osgi;
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
- <attributes>
- <attribute name="module" value="true"/>
- </attributes>
- </classpathentry>
- <classpathentry kind="output" path="bin"/>
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>org.argeo.osgi.boot</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.pde.ManifestBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- <nature>org.eclipse.pde.PluginNature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-/MANIFEST.MF
+++ /dev/null
-Main-Class: org.argeo.osgi.boot.Main
-Class-Path: org.eclipse.osgi.jar
-
-Bundle-Activator: org.argeo.osgi.boot.Activator
-Import-Package: org.eclipse.*;resolution:=optional,\
-org.eclipse.osgi.launch.*;resolution:=optional,\
-org.osgi.*;version=0.0.0,\
-*
+++ /dev/null
-source.. = src/,\
- ext/test/
-additional.bundles = org.junit,\
- org.hamcrest
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.argeo.commons</groupId>
- <version>2.3-SNAPSHOT</version>
- <artifactId>argeo-commons</artifactId>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>org.argeo.osgi.boot</artifactId>
- <packaging>jar</packaging>
- <name>OSGi Boot</name>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <skipTests>true</skipTests>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <dependencies>
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>argeo-tp</artifactId> -->
-<!-- <version>${version.argeo-tp}</version> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp.rap.platform</groupId> -->
-<!-- <artifactId>org.eclipse.osgi</artifactId> -->
-<!-- <scope>provided</scope> -->
-<!-- </dependency> -->
-
- <!-- TEST -->
-<!-- <dependency> -->
-<!-- <groupId>org.argeo.tp</groupId> -->
-<!-- <artifactId>junit</artifactId> -->
-<!-- <scope>test</scope> -->
-<!-- </dependency> -->
- </dependencies>
-
-
-</project>
\ No newline at end of file
+++ /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.Files;
-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);
- if (Files.exists(base)) {
- FsA2Source source = new FsA2Source(base);
- source.load();
- addSource(source);
- OsgiBootUtils.info("Registered " + uri + " as 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.debug("Default source from framework location " + frameworkLocation);
- 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);
-
-}
+++ /dev/null
-/** A2 OSGi repository format. */
-package org.argeo.osgi.a2;
\ No newline at end of file
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-
-/**
- * An OSGi configurator. See
- * <a href="http://wiki.eclipse.org/Configurator">http:
- * //wiki.eclipse.org/Configurator</a>
- */
-public class Activator implements BundleActivator {
- private Long checkpoint = null;
-
- public void start(final BundleContext bundleContext) throws Exception {
- // admin thread
- Thread adminThread = new AdminThread(bundleContext);
- adminThread.start();
-
- // bootstrap
- OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
- if (checkpoint == null) {
- osgiBoot.bootstrap();
- checkpoint = System.currentTimeMillis();
- } else {
- osgiBoot.update();
- checkpoint = System.currentTimeMillis();
- }
- }
-
- public void stop(BundleContext context) throws Exception {
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.io.File;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.launch.Framework;
-
-/** Monitors the runtime and can shut it down. */
-public class AdminThread extends Thread {
- public final static String PROP_ARGEO_OSGI_SHUTDOWN_FILE = "argeo.osgi.shutdownFile";
- private File shutdownFile;
- private final BundleContext bundleContext;
-
- public AdminThread(BundleContext bundleContext) {
- super("OSGi Boot Admin");
- this.bundleContext = bundleContext;
- if (System.getProperty(PROP_ARGEO_OSGI_SHUTDOWN_FILE) != null) {
- shutdownFile = new File(
- System.getProperty(PROP_ARGEO_OSGI_SHUTDOWN_FILE));
- if (!shutdownFile.exists()) {
- shutdownFile = null;
- OsgiBootUtils.warn("Shutdown file " + shutdownFile
- + " not found, feature deactivated");
- }
- }
- }
-
- public void run() {
- if (shutdownFile != null) {
- // wait for file to be removed
- while (shutdownFile.exists()) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- Framework framework = (Framework) bundleContext.getBundle(0);
- try {
- // shutdown framework
- framework.stop();
- // wait 10 mins for shutdown
- framework.waitForStop(10 * 60 * 1000);
- // close VM
- System.exit(0);
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- }
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.StringTokenizer;
-
-/** Intermediary structure used by path matching */
-class BundlesSet {
- private String baseUrl = "reference:file";// not used yet
- private final String dir;
- private List<String> includes = new ArrayList<String>();
- private List<String> excludes = new ArrayList<String>();
-
- public BundlesSet(String def) {
- StringTokenizer st = new StringTokenizer(def, ";");
-
- if (!st.hasMoreTokens())
- throw new RuntimeException("Base dir not defined.");
- try {
- String dirPath = st.nextToken();
-
- if (dirPath.startsWith("file:"))
- dirPath = dirPath.substring("file:".length());
-
- dir = new File(dirPath.replace('/', File.separatorChar)).getCanonicalPath();
- if (OsgiBootUtils.debug)
- OsgiBootUtils.debug("Base dir: " + dir);
- } catch (IOException e) {
- throw new RuntimeException("Cannot convert to absolute path", e);
- }
-
- while (st.hasMoreTokens()) {
- String tk = st.nextToken();
- StringTokenizer stEq = new StringTokenizer(tk, "=");
- String type = stEq.nextToken();
- String pattern = stEq.nextToken();
- if ("in".equals(type) || "include".equals(type)) {
- includes.add(pattern);
- } else if ("ex".equals(type) || "exclude".equals(type)) {
- excludes.add(pattern);
- } else if ("baseUrl".equals(type)) {
- baseUrl = pattern;
- } else {
- System.err.println("Unkown bundles pattern type " + type);
- }
- }
-
- // if (excludeSvn && !excludes.contains(EXCLUDES_SVN_PATTERN)) {
- // excludes.add(EXCLUDES_SVN_PATTERN);
- // }
- }
-
- public String getDir() {
- return dir;
- }
-
- public List<String> getIncludes() {
- return includes;
- }
-
- public List<String> getExcludes() {
- return excludes;
- }
-
- public String getBaseUrl() {
- return baseUrl;
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.PathMatcher;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.SortedMap;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import java.util.jar.Manifest;
-
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-
-/**
- * A distribution bundle is a bundle within a maven-like distribution
- * groupId:Bundle-SymbolicName:Bundle-Version which references others OSGi
- * bundle. It is not required to be OSGi complete also it will generally be
- * expected that it is. The root of the repository is computed based on the file
- * name of the URL and of the content of the index.
- */
-public class DistributionBundle {
- private final static String INDEX_FILE_NAME = "modularDistribution.csv";
-
- private final String url;
-
- private Manifest manifest;
- private String symbolicName;
- private String version;
-
- /** can be null */
- private String baseUrl;
- /** can be null */
- private String relativeUrl;
- private String localCache;
-
- private List<OsgiArtifact> artifacts;
-
- private String separator = ",";
-
- public DistributionBundle(String url) {
- this.url = url;
- }
-
- public DistributionBundle(String baseUrl, String relativeUrl, String localCache) {
- if (baseUrl == null || !baseUrl.endsWith("/"))
- throw new OsgiBootException("Base url " + baseUrl + " badly formatted");
- if (relativeUrl.startsWith("http") || relativeUrl.startsWith("file:"))
- throw new OsgiBootException("Relative URL " + relativeUrl + " badly formatted");
- this.url = constructUrl(baseUrl, relativeUrl);
- this.baseUrl = baseUrl;
- this.relativeUrl = relativeUrl;
- this.localCache = localCache;
- }
-
- protected String constructUrl(String baseUrl, String relativeUrl) {
- try {
- if (relativeUrl.indexOf('*') >= 0) {
- if (!baseUrl.startsWith("file:"))
- throw new IllegalArgumentException(
- "Wildcard support only for file:, badly formatted " + baseUrl + " and " + relativeUrl);
- Path basePath = Paths.get(new URI(baseUrl));
- // Path basePath = Paths.get(new URI(baseUrl));
- // int li = relativeUrl.lastIndexOf('/');
- // String relativeDir = relativeUrl.substring(0, li);
- // String relativeFile = relativeUrl.substring(li,
- // relativeUrl.length());
- String pattern = "glob:" + basePath + '/' + relativeUrl;
- PathMatcher pm = basePath.getFileSystem().getPathMatcher(pattern);
- SortedMap<Version, Path> res = new TreeMap<>();
- checkDir(basePath, pm, res);
- if (res.size() == 0)
- throw new OsgiBootException("No file matching " + relativeUrl + " found in " + baseUrl);
- return res.get(res.firstKey()).toUri().toString();
- } else {
- return baseUrl + relativeUrl;
- }
- } catch (Exception e) {
- throw new OsgiBootException("Cannot build URL from " + baseUrl + " and " + relativeUrl, e);
- }
- }
-
- private void checkDir(Path dir, PathMatcher pm, SortedMap<Version, Path> res) throws IOException {
- try (DirectoryStream<Path> ds = Files.newDirectoryStream(dir)) {
- for (Path path : ds) {
- if (Files.isDirectory(path))
- checkDir(path, pm, res);
- else if (pm.matches(path)) {
- String fileName = path.getFileName().toString();
- fileName = fileName.substring(0, fileName.lastIndexOf('.'));
- if (fileName.endsWith("-SNAPSHOT"))
- fileName = fileName.substring(0, fileName.lastIndexOf('-')) + ".SNAPSHOT";
- fileName = fileName.substring(fileName.lastIndexOf('-') + 1);
- Version version = new Version(fileName);
- res.put(version, path);
- }
- }
- }
- }
-
- public void processUrl() {
- JarInputStream jarIn = null;
- try {
- URL u = new URL(url);
-
- // local cache
- URI localUri = new URI(localCache + relativeUrl);
- Path localPath = Paths.get(localUri);
- if (Files.exists(localPath))
- u = localUri.toURL();
- jarIn = new JarInputStream(u.openStream());
-
- // meta data
- manifest = jarIn.getManifest();
- symbolicName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
- version = manifest.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
-
- JarEntry indexEntry;
- while ((indexEntry = jarIn.getNextJarEntry()) != null) {
- String entryName = indexEntry.getName();
- if (entryName.equals(INDEX_FILE_NAME)) {
- break;
- }
- jarIn.closeEntry();
- }
-
- // list artifacts
- if (indexEntry == null)
- throw new OsgiBootException("No index " + INDEX_FILE_NAME + " in " + url);
- artifacts = listArtifacts(jarIn);
- jarIn.closeEntry();
-
- // find base URL
- // won't work if distribution artifact is not listed
- for (int i = 0; i < artifacts.size(); i++) {
- OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
- if (osgiArtifact.getSymbolicName().equals(symbolicName) && osgiArtifact.getVersion().equals(version)) {
- String relativeUrl = osgiArtifact.getRelativeUrl();
- if (url.endsWith(relativeUrl)) {
- baseUrl = url.substring(0, url.length() - osgiArtifact.getRelativeUrl().length());
- break;
- }
- }
- }
- } catch (Exception e) {
- throw new OsgiBootException("Cannot list URLs from " + url, e);
- } finally {
- if (jarIn != null)
- try {
- jarIn.close();
- } catch (IOException e) {
- // silent
- }
- }
- }
-
- protected List<OsgiArtifact> listArtifacts(InputStream in) {
- List<OsgiArtifact> osgiArtifacts = new ArrayList<OsgiArtifact>();
- BufferedReader reader = null;
- try {
- reader = new BufferedReader(new InputStreamReader(in));
- String line = null;
- lines: while ((line = reader.readLine()) != null) {
- StringTokenizer st = new StringTokenizer(line, separator);
- String moduleName = st.nextToken();
- String moduleVersion = st.nextToken();
- String relativeUrl = st.nextToken();
- if (relativeUrl.endsWith(".pom"))
- continue lines;
- osgiArtifacts.add(new OsgiArtifact(moduleName, moduleVersion, relativeUrl));
- }
- } catch (Exception e) {
- throw new OsgiBootException("Cannot list artifacts", e);
- }
- return osgiArtifacts;
- }
-
- /** Convenience method */
- public static DistributionBundle processUrl(String baseUrl, String relativeUrl, String localCache) {
- DistributionBundle distributionBundle = new DistributionBundle(baseUrl, relativeUrl, localCache);
- distributionBundle.processUrl();
- return distributionBundle;
- }
-
- /**
- * List full URLs of the bundles, based on base URL, usable directly for
- * download.
- */
- public List<String> listUrls() {
- if (baseUrl == null)
- throw new OsgiBootException("Base URL is not set");
-
- if (artifacts == null)
- throw new OsgiBootException("Artifact list not initialized");
-
- List<String> urls = new ArrayList<String>();
- for (int i = 0; i < artifacts.size(); i++) {
- OsgiArtifact osgiArtifact = (OsgiArtifact) artifacts.get(i);
- // local cache
- URI localUri;
- try {
- localUri = new URI(localCache + relativeUrl);
- } catch (URISyntaxException e) {
- OsgiBootUtils.warn(e.getMessage());
- localUri = null;
- }
- Version version = new Version(osgiArtifact.getVersion());
- if (localUri != null && Files.exists(Paths.get(localUri)) && version.getQualifier() != null
- && version.getQualifier().startsWith("SNAPSHOT")) {
- urls.add(localCache + osgiArtifact.getRelativeUrl());
- } else {
- urls.add(baseUrl + osgiArtifact.getRelativeUrl());
- }
- }
- return urls;
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
- /** Separator used to parse the tabular file */
- public void setSeparator(String modulesUrlSeparator) {
- this.separator = modulesUrlSeparator;
- }
-
- public String getRelativeUrl() {
- return relativeUrl;
- }
-
- /** One of the listed artifact */
- protected static class OsgiArtifact {
- private final String symbolicName;
- private final String version;
- private final String relativeUrl;
-
- public OsgiArtifact(String symbolicName, String version, String relativeUrl) {
- super();
- this.symbolicName = symbolicName;
- this.version = version;
- this.relativeUrl = relativeUrl;
- }
-
- public String getSymbolicName() {
- return symbolicName;
- }
-
- public String getVersion() {
- return version;
- }
-
- public String getRelativeUrl() {
- return relativeUrl;
- }
-
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.core.runtime.adaptor.EclipseStarter;
-import org.osgi.framework.BundleContext;
-
-/** Command line interface. */
-public class Launcher {
-
- public static void main(String[] args) {
- // Try to load system properties
- String systemPropertiesFilePath = getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE);
- if (systemPropertiesFilePath != null) {
- FileInputStream in;
- try {
- in = new FileInputStream(systemPropertiesFilePath);
- System.getProperties().load(in);
- } catch (IOException e1) {
- throw new RuntimeException("Cannot load system properties from " + systemPropertiesFilePath, e1);
- }
- if (in != null) {
- try {
- in.close();
- } catch (Exception e) {
- // silent
- }
- }
- }
-
- // Start main class
- startMainClass();
-
- // Start Equinox
- BundleContext bundleContext = null;
- try {
- bundleContext = EclipseStarter.startup(args, null);
- } catch (Exception e) {
- throw new RuntimeException("Cannot start Equinox.", e);
- }
-
- // OSGi bootstrap
- OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
- osgiBoot.bootstrap();
- }
-
- protected static void startMainClass() {
- String className = getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS);
- if (className == null)
- return;
-
- String line = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPARGS, "");
-
- String[] uiArgs = readArgumentsFromLine(line);
-
- try {
- // Launch main method using reflection
- Class<?> clss = Class.forName(className);
- Class<?>[] mainArgsClasses = new Class[] { uiArgs.getClass() };
- Object[] mainArgs = { uiArgs };
- Method mainMethod = clss.getMethod("main", mainArgsClasses);
- mainMethod.invoke(null, mainArgs);
- } catch (Exception e) {
- throw new RuntimeException("Cannot start main class.", e);
- }
-
- }
-
- /**
- * Transform a line into an array of arguments, taking "" as single
- * arguments. (nested \" are not supported)
- */
- private static String[] readArgumentsFromLine(String lineOrig) {
- String line = lineOrig.trim();// remove trailing spaces
- List<String> args = new ArrayList<String>();
- StringBuffer curr = new StringBuffer("");
- boolean inQuote = false;
- char[] arr = line.toCharArray();
- for (int i = 0; i < arr.length; i++) {
- char c = arr[i];
- switch (c) {
- case '\"':
- inQuote = !inQuote;
- break;
- case ' ':
- if (!inQuote) {// otherwise, no break: goes to default
- if (curr.length() > 0) {
- args.add(curr.toString());
- curr = new StringBuffer("");
- }
- break;
- }
- default:
- curr.append(c);
- break;
- }
- }
-
- // Add last arg
- if (curr.length() > 0) {
- args.add(curr.toString());
- curr = null;
- }
-
- String[] res = new String[args.size()];
- for (int i = 0; i < args.size(); i++) {
- res[i] = args.get(i).toString();
- }
- return res;
- }
-
- public static String getProperty(String name, String defaultValue) {
- final String value;
- if (defaultValue != null)
- value = System.getProperty(name, defaultValue);
- else
- value = System.getProperty(name);
-
- if (value == null || value.equals(""))
- return null;
- else
- return value;
- }
-
- public static String getProperty(String name) {
- return getProperty(name, null);
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.lang.management.ManagementFactory;
-
-public class Main {
-
- public static void main(String[] args) {
- String mainClass = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS);
- if (mainClass == null) {
- throw new IllegalArgumentException(
- "System property " + OsgiBoot.PROP_ARGEO_OSGI_BOOT_APPCLASS + " must be specified");
- }
-
- OsgiBuilder osgi = new OsgiBuilder();
- String distributionUrl = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_DISTRIBUTION_URL);
- if (distributionUrl != null)
- osgi.install(distributionUrl);
- // osgi.conf("argeo.node.useradmin.uris", "os:///");
- // osgi.conf("osgi.clean", "true");
- // osgi.conf("osgi.console", "true");
- osgi.launch();
-
- if (OsgiBootUtils.isDebug()) {
- long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
- String jvmUptimeStr = (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s";
- OsgiBootUtils.debug("Ready to launch " + mainClass + " in " + jvmUptimeStr);
- }
-
- osgi.main(mainClass, args);
-
- osgi.shutdown();
-
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-import java.util.ServiceLoader;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.launch.Framework;
-import org.osgi.framework.launch.FrameworkFactory;
-
-/** Launch an OSGi framework and deploy a CMS Node into it. */
-public class NodeRunner {
- private Long timeout = 30 * 1000l;
- private final Path baseDir;
- private final Path confDir;
- private final Path dataDir;
-
- private String baseUrl = "http://forge.argeo.org/data/java/argeo-2.1/";
- private String distributionUrl = null;
-
- private Framework framework = null;
-
- public NodeRunner(String distributionUrl, Path baseDir) {
- this.distributionUrl = distributionUrl;
- Path mavenBase = Paths.get(System.getProperty("user.home") + "/.m2/repository");
- Path osgiBase = Paths.get("/user/share/osgi");
- if (Files.exists(mavenBase)) {
- Path mavenPath = mavenBase.resolve(distributionUrl);
- if (Files.exists(mavenPath))
- baseUrl = mavenBase.toUri().toString();
- } else if (Files.exists(osgiBase)) {
- Path osgiPath = osgiBase.resolve(distributionUrl);
- if (Files.exists(osgiPath))
- baseUrl = osgiBase.toUri().toString();
- }
-
- this.baseDir = baseDir;
- this.confDir = this.baseDir.resolve("state");
- this.dataDir = this.baseDir.resolve("data");
-
- }
-
- public void start() {
- long begin = System.currentTimeMillis();
- // log4j
- Path log4jFile = confDir.resolve("log4j.properties");
- if (!Files.exists(log4jFile))
- copyResource("/org/argeo/osgi/boot/log4j.properties", log4jFile);
- System.setProperty("log4j.configuration", "file://" + log4jFile.toAbsolutePath());
-
- // Start Equinox
- try {
- ServiceLoader<FrameworkFactory> ff = ServiceLoader.load(FrameworkFactory.class);
- FrameworkFactory frameworkFactory = ff.iterator().next();
- Map<String, String> configuration = new HashMap<String, String>();
- configuration.put("osgi.configuration.area", confDir.toAbsolutePath().toString());
- configuration.put("osgi.instance.area", dataDir.toAbsolutePath().toString());
- defaultConfiguration(configuration);
-
- framework = frameworkFactory.newFramework(configuration);
- framework.start();
- info("## Date : " + new Date());
- info("## Data : " + dataDir.toAbsolutePath());
- } catch (Exception e) {
- throw new IllegalStateException("Cannot start OSGi framework", e);
- }
- BundleContext bundleContext = framework.getBundleContext();
- try {
-
- // Spring configs currently require System properties
- // System.getProperties().putAll(configuration);
-
- // expected by JAAS as System.property FIXME
- System.setProperty("osgi.instance.area", bundleContext.getProperty("osgi.instance.area"));
-
- // OSGi bootstrap
- OsgiBoot osgiBoot = new OsgiBoot(bundleContext);
-
- osgiBoot.installUrls(osgiBoot.getDistributionUrls(distributionUrl, baseUrl));
-
- // Start runtime
- Properties startProperties = new Properties();
- // TODO make it possible to override it
- startProperties.put("argeo.osgi.start.2.node",
- "org.eclipse.equinox.http.servlet,org.eclipse.equinox.http.jetty,"
- + "org.eclipse.equinox.metatype,org.eclipse.equinox.cm,org.eclipse.rap.rwt.osgi");
- startProperties.put("argeo.osgi.start.3.node", "org.argeo.cms");
- startProperties.put("argeo.osgi.start.4.node",
- "org.eclipse.gemini.blueprint.extender,org.eclipse.equinox.http.registry");
- osgiBoot.startBundles(startProperties);
-
- // Find node repository
- ServiceReference<?> sr = null;
- while (sr == null) {
- sr = bundleContext.getServiceReference("javax.jcr.Repository");
- if (System.currentTimeMillis() - begin > timeout)
- throw new RuntimeException("Could find node after " + timeout + "ms");
- Thread.sleep(100);
- }
- Object nodeDeployment = bundleContext.getService(sr);
- info("Node Deployment " + nodeDeployment);
-
- // Initialization completed
- long duration = System.currentTimeMillis() - begin;
- info("## CMS Launcher initialized in " + (duration / 1000) + "s " + (duration % 1000) + "ms");
- } catch (Exception e) {
- shutdown();
- throw new RuntimeException("Cannot start CMS", e);
- } finally {
-
- }
- }
-
- private void defaultConfiguration(Map<String, String> configuration) {
- // all permissions to OSGi security manager
- Path policyFile = confDir.resolve("node.policy");
- if (!Files.exists(policyFile))
- copyResource("/org/argeo/osgi/boot/node.policy", policyFile);
- configuration.put("java.security.policy", "file://" + policyFile.toAbsolutePath());
-
- configuration.put("org.eclipse.rap.workbenchAutostart", "false");
- configuration.put("org.eclipse.equinox.http.jetty.autostart", "false");
- 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");
-
- // Do clean
- // configuration.put("osgi.clean", "true");
- // if (args.length == 0) {
- // configuration.put("osgi.console", "");
- // }
- }
-
- public void shutdown() {
- try {
- framework.stop();
- framework.waitForStop(15 * 1000);
- } catch (Exception silent) {
- }
- }
-
- public Path getConfDir() {
- return confDir;
- }
-
- public Path getDataDir() {
- return dataDir;
- }
-
- public Framework getFramework() {
- return framework;
- }
-
- public static void main(String[] args) {
- try {
- String distributionUrl;
- Path executionDir;
- if (args.length == 2) {
- distributionUrl = args[0];
- executionDir = Paths.get(args[1]);
- } else if (args.length == 1) {
- executionDir = Paths.get(System.getProperty("user.dir"));
- distributionUrl = args[0];
- } else if (args.length == 0) {
- executionDir = Paths.get(System.getProperty("user.dir"));
- distributionUrl = "org/argeo/commons/org.argeo.dep.cms.sdk/2.1.70/org.argeo.dep.cms.sdk-2.1.70.jar";
- }else{
- printUsage();
- System.exit(1);
- return;
- }
-
- NodeRunner nodeRunner = new NodeRunner(distributionUrl, executionDir);
- nodeRunner.start();
-// if (args.length != 0)
-// System.exit(0);
- } catch (Exception e) {
- e.printStackTrace();
- System.exit(1);
- }
- }
-
- protected static void info(Object msg) {
- System.out.println(msg);
- }
-
- protected static void err(Object msg) {
- System.err.println(msg);
- }
-
- protected static void debug(Object msg) {
- System.out.println(msg);
- }
-
- protected static void copyResource(String resource, Path targetFile) {
- InputStream input = null;
- OutputStream output = null;
- try {
- input = NodeRunner.class.getResourceAsStream(resource);
- Files.createDirectories(targetFile.getParent());
- output = Files.newOutputStream(targetFile);
- byte[] buf = new byte[8192];
- while (true) {
- int length = input.read(buf);
- if (length < 0)
- break;
- output.write(buf, 0, length);
- }
- } catch (Exception e) {
- throw new RuntimeException("Cannot write " + resource + " file to " + targetFile, e);
- } finally {
- try {
- input.close();
- } catch (Exception ignore) {
- }
- try {
- output.close();
- } catch (Exception ignore) {
- }
- }
-
- }
-
- static void printUsage(){
- err("Usage: <distribution url> <base dir>");
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import static org.argeo.osgi.boot.OsgiBootUtils.debug;
-import static org.argeo.osgi.boot.OsgiBootUtils.warn;
-
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-
-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;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.Version;
-import org.osgi.framework.startlevel.BundleStartLevel;
-import org.osgi.framework.startlevel.FrameworkStartLevel;
-import org.osgi.framework.wiring.FrameworkWiring;
-
-/**
- * Basic provisioning of an OSGi runtime via file path patterns and system
- * properties. The approach is to generate list of URLs based on various
- * methods, configured via properties.
- */
-public class OsgiBoot implements OsgiBootConstants {
- public final static String PROP_ARGEO_OSGI_START = "argeo.osgi.start";
- public final static String PROP_ARGEO_OSGI_SOURCES = "argeo.osgi.sources";
-
- public final static String PROP_ARGEO_OSGI_BUNDLES = "argeo.osgi.bundles";
- public final static String PROP_ARGEO_OSGI_BASE_URL = "argeo.osgi.baseUrl";
- public final static String PROP_ARGEO_OSGI_LOCAL_CACHE = "argeo.osgi.localCache";
- public final static String PROP_ARGEO_OSGI_DISTRIBUTION_URL = "argeo.osgi.distributionUrl";
-
- // booleans
- public final static String PROP_ARGEO_OSGI_BOOT_DEBUG = "argeo.osgi.boot.debug";
- // public final static String PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN =
- // "argeo.osgi.boot.excludeSvn";
-
- public final static String PROP_ARGEO_OSGI_BOOT_SYSTEM_PROPERTIES_FILE = "argeo.osgi.boot.systemPropertiesFile";
- public final static String PROP_ARGEO_OSGI_BOOT_APPCLASS = "argeo.osgi.boot.appclass";
- public final static String PROP_ARGEO_OSGI_BOOT_APPARGS = "argeo.osgi.boot.appargs";
-
- public final static String DEFAULT_BASE_URL = "reference:file:";
- // public final static String EXCLUDES_SVN_PATTERN = "**/.svn/**";
-
- // OSGi system properties
- final static String PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL = "osgi.bundles.defaultStartLevel";
- final static String PROP_OSGI_STARTLEVEL = "osgi.startLevel";
- final static String INSTANCE_AREA_PROP = "osgi.instance.area";
- final static String CONFIGURATION_AREA_PROP = "osgi.configuration.area";
-
- // Symbolic names
- public final static String SYMBOLIC_NAME_OSGI_BOOT = "org.argeo.osgi.boot";
- public final static String SYMBOLIC_NAME_EQUINOX = "org.eclipse.osgi";
-
- /** Exclude svn metadata implicitely(a bit costly) */
- // private boolean excludeSvn =
- // Boolean.valueOf(System.getProperty(PROP_ARGEO_OSGI_BOOT_EXCLUDE_SVN,
- // "false"))
- // .booleanValue();
-
- /** Default is 10s */
- @Deprecated
- private long defaultTimeout = 10000l;
-
- private final BundleContext bundleContext;
- private final String localCache;
-
- private final ProvisioningManager provisioningManager;
-
- /*
- * INITIALIZATION
- */
- /** Constructor */
- public OsgiBoot(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- 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);
- String sources = getProperty(PROP_ARGEO_OSGI_SOURCES);
- if (sources == null) {
- provisioningManager.registerDefaultSource();
- } else {
- for (String source : sources.split(",")) {
- if (source.trim().equals(A2Source.DEFAULT_A2_URI)) {
- if (Files.exists(homePath))
- 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);
- }
- }
- }
- }
-
- ProvisioningManager getProvisioningManager() {
- return provisioningManager;
- }
-
- /*
- * HIGH-LEVEL METHODS
- */
- /** Bootstraps the OSGi runtime */
- public void bootstrap() {
- try {
- long begin = System.currentTimeMillis();
- System.out.println();
- String osgiInstancePath = bundleContext.getProperty(INSTANCE_AREA_PROP);
- OsgiBootUtils
- .info("OSGi bootstrap starting" + (osgiInstancePath != null ? " (" + osgiInstancePath + ")" : ""));
- installUrls(getBundlesUrls());
- installUrls(getDistributionUrls());
- provisioningManager.install(null);
- startBundles();
- long duration = System.currentTimeMillis() - begin;
- OsgiBootUtils.info("OSGi bootstrap completed in " + Math.round(((double) duration) / 1000) + "s ("
- + duration + "ms), " + bundleContext.getBundles().length + " bundles");
- } catch (RuntimeException e) {
- OsgiBootUtils.error("OSGi bootstrap FAILED", e);
- throw e;
- }
-
- // diagnostics
- if (OsgiBootUtils.debug) {
- OsgiBootDiagnostics diagnostics = new OsgiBootDiagnostics(bundleContext);
- diagnostics.checkUnresolved();
- Map<String, Set<String>> duplicatePackages = diagnostics.findPackagesExportedTwice();
- if (duplicatePackages.size() > 0) {
- OsgiBootUtils.info("Packages exported twice:");
- Iterator<String> it = duplicatePackages.keySet().iterator();
- while (it.hasNext()) {
- String pkgName = it.next();
- OsgiBootUtils.info(pkgName);
- Set<String> bdles = duplicatePackages.get(pkgName);
- Iterator<String> bdlesIt = bdles.iterator();
- while (bdlesIt.hasNext())
- OsgiBootUtils.info(" " + bdlesIt.next());
- }
- }
- }
- System.out.println();
- }
-
- public void update() {
- provisioningManager.update();
- }
-
- /*
- * INSTALLATION
- */
- /** Install a single url. Convenience method. */
- public Bundle installUrl(String url) {
- List<String> urls = new ArrayList<String>();
- urls.add(url);
- installUrls(urls);
- return (Bundle) getBundlesByLocation().get(url);
- }
-
- /** Install the bundles at this URL list. */
- public void installUrls(List<String> urls) {
- Map<String, Bundle> installedBundles = getBundlesByLocation();
- for (int i = 0; i < urls.size(); i++) {
- String url = (String) urls.get(i);
- installUrl(url, installedBundles);
- }
- refreshFramework();
- }
-
- /** Actually install the provided URL */
- protected void installUrl(String url, Map<String, Bundle> installedBundles) {
- try {
- if (installedBundles.containsKey(url)) {
- Bundle bundle = (Bundle) installedBundles.get(url);
- if (OsgiBootUtils.debug)
- debug("Bundle " + bundle.getSymbolicName() + " already installed from " + url);
- } else if (url.contains("/" + SYMBOLIC_NAME_EQUINOX + "/")
- || url.contains("/" + SYMBOLIC_NAME_OSGI_BOOT + "/")) {
- if (OsgiBootUtils.debug)
- warn("Skip " + url);
- return;
- } else {
- Bundle bundle = bundleContext.installBundle(url);
- if (url.startsWith("http"))
- OsgiBootUtils
- .info("Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
- else if (OsgiBootUtils.debug)
- OsgiBootUtils.debug(
- "Installed " + bundle.getSymbolicName() + "-" + bundle.getVersion() + " from " + url);
- assert bundle.getSymbolicName() != null;
- // uninstall previous versions
- bundles: for (Bundle b : bundleContext.getBundles()) {
- if (b.getSymbolicName() == null)
- continue bundles;
- if (bundle.getSymbolicName().equals(b.getSymbolicName())) {
- Version bundleV = bundle.getVersion();
- Version bV = b.getVersion();
- if (bV == null)
- continue bundles;
- if (bundleV.getMajor() == bV.getMajor() && bundleV.getMinor() == bV.getMinor()) {
- if (bundleV.getMicro() > bV.getMicro()) {
- // uninstall older bundles
- b.uninstall();
- OsgiBootUtils.debug("Uninstalled " + b);
- } else if (bundleV.getMicro() < bV.getMicro()) {
- // uninstall just installed bundle if newer
- bundle.uninstall();
- OsgiBootUtils.debug("Uninstalled " + bundle);
- break bundles;
- } else {
- // uninstall any other with same major/minor
- if (!bundleV.getQualifier().equals(bV.getQualifier())) {
- b.uninstall();
- OsgiBootUtils.debug("Uninstalled " + b);
- }
- }
- }
- }
- }
- }
- } catch (BundleException e) {
- final String ALREADY_INSTALLED = "is already installed";
- String message = e.getMessage();
- if ((message.contains("Bundle \"" + SYMBOLIC_NAME_OSGI_BOOT + "\"")
- || message.contains("Bundle \"" + SYMBOLIC_NAME_EQUINOX + "\""))
- && message.contains(ALREADY_INSTALLED)) {
- // silent, in order to avoid warnings: we know that both
- // have already been installed...
- } else {
- if (message.contains(ALREADY_INSTALLED)) {
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.warn("Duplicate install from " + url + ": " + message);
- } else
- OsgiBootUtils.warn("Could not install bundle from " + url + ": " + message);
- }
- if (OsgiBootUtils.debug && !message.contains(ALREADY_INSTALLED))
- e.printStackTrace();
- }
- }
-
- /*
- * START
- */
- public void startBundles() {
- startBundles(System.getProperties());
- }
-
- public void startBundles(Properties properties) {
- FrameworkStartLevel frameworkStartLevel = bundleContext.getBundle(0).adapt(FrameworkStartLevel.class);
-
- // default and active start levels from System properties
- Integer defaultStartLevel = new Integer(
- Integer.parseInt(getProperty(PROP_OSGI_BUNDLES_DEFAULTSTARTLEVEL, "4")));
- Integer activeStartLevel = new Integer(getProperty(PROP_OSGI_STARTLEVEL, "6"));
-
- SortedMap<Integer, List<String>> startLevels = new TreeMap<Integer, List<String>>();
- computeStartLevels(startLevels, properties, defaultStartLevel);
- // inverts the map for the time being, TODO optimise
- Map<String, Integer> bundleStartLevels = new HashMap<>();
- for (Integer level : startLevels.keySet()) {
- for (String bsn : startLevels.get(level))
- bundleStartLevels.put(bsn, level);
- }
- for (Bundle bundle : bundleContext.getBundles()) {
- String bsn = bundle.getSymbolicName();
- if (bundleStartLevels.containsKey(bsn)) {
- BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class);
- Integer level = bundleStartLevels.get(bsn);
- if (bundleStartLevel.getStartLevel() != level || !bundleStartLevel.isPersistentlyStarted()) {
- bundleStartLevel.setStartLevel(level);
- try {
- bundle.start();
- } catch (BundleException e) {
- OsgiBootUtils.error("Cannot mark " + bsn + " as started", e);
- }
- if (getDebug())
- OsgiBootUtils.debug(bsn + " starts at level " + level);
- }
- }
- }
- frameworkStartLevel.setStartLevel(activeStartLevel, (FrameworkEvent event) -> {
- if (getDebug())
- OsgiBootUtils.debug("Framework event: " + event);
- int initialStartLevel = frameworkStartLevel.getInitialBundleStartLevel();
- int startLevel = frameworkStartLevel.getStartLevel();
- OsgiBootUtils.debug("Framework start level: " + startLevel + " (initial: " + initialStartLevel + ")");
- });
- }
-
- private static void computeStartLevels(SortedMap<Integer, List<String>> startLevels, Properties properties,
- Integer defaultStartLevel) {
-
- // default (and previously, only behaviour)
- appendToStartLevels(startLevels, defaultStartLevel, properties.getProperty(PROP_ARGEO_OSGI_START, ""));
-
- // list argeo.osgi.start.* system properties
- Iterator<Object> keys = properties.keySet().iterator();
- final String prefix = PROP_ARGEO_OSGI_START + ".";
- while (keys.hasNext()) {
- String key = keys.next().toString();
- if (key.startsWith(prefix)) {
- Integer startLevel;
- String suffix = key.substring(prefix.length());
- String[] tokens = suffix.split("\\.");
- if (tokens.length > 0 && !tokens[0].trim().equals(""))
- try {
- // first token is start level
- startLevel = new Integer(tokens[0]);
- } catch (NumberFormatException e) {
- startLevel = defaultStartLevel;
- }
- else
- startLevel = defaultStartLevel;
-
- // append bundle names
- String bundleNames = properties.getProperty(key);
- appendToStartLevels(startLevels, startLevel, bundleNames);
- }
- }
- }
-
- /** Append a comma-separated list of bundles to the start levels. */
- private static void appendToStartLevels(SortedMap<Integer, List<String>> startLevels, Integer startLevel,
- String str) {
- if (str == null || str.trim().equals(""))
- return;
-
- if (!startLevels.containsKey(startLevel))
- startLevels.put(startLevel, new ArrayList<String>());
- String[] bundleNames = str.split(",");
- for (int i = 0; i < bundleNames.length; i++) {
- if (bundleNames[i] != null && !bundleNames[i].trim().equals(""))
- (startLevels.get(startLevel)).add(bundleNames[i]);
- }
- }
-
- /**
- * Start the provided list of bundles
- *
- * @return whether all bundles are now in active state
- * @deprecated
- */
- @Deprecated
- public boolean startBundles(List<String> bundlesToStart) {
- if (bundlesToStart.size() == 0)
- return true;
-
- // used to monitor ACTIVE states
- List<Bundle> startedBundles = new ArrayList<Bundle>();
- // used to log the bundles not found
- List<String> notFoundBundles = new ArrayList<String>(bundlesToStart);
-
- Bundle[] bundles = bundleContext.getBundles();
- long startBegin = System.currentTimeMillis();
- for (int i = 0; i < bundles.length; i++) {
- Bundle bundle = bundles[i];
- String symbolicName = bundle.getSymbolicName();
- if (bundlesToStart.contains(symbolicName))
- try {
- try {
- bundle.start();
- if (OsgiBootUtils.debug)
- debug("Bundle " + symbolicName + " started");
- } catch (Exception e) {
- OsgiBootUtils.warn("Start of bundle " + symbolicName + " failed because of " + e
- + ", maybe bundle is not yet resolved," + " waiting and trying again.");
- waitForBundleResolvedOrActive(startBegin, bundle);
- bundle.start();
- startedBundles.add(bundle);
- }
- notFoundBundles.remove(symbolicName);
- } catch (Exception e) {
- OsgiBootUtils.warn("Bundle " + symbolicName + " cannot be started: " + e.getMessage());
- if (OsgiBootUtils.debug)
- e.printStackTrace();
- // was found even if start failed
- notFoundBundles.remove(symbolicName);
- }
- }
-
- for (int i = 0; i < notFoundBundles.size(); i++)
- OsgiBootUtils.warn("Bundle '" + notFoundBundles.get(i) + "' not started because it was not found.");
-
- // monitors that all bundles are started
- long beginMonitor = System.currentTimeMillis();
- boolean allStarted = !(startedBundles.size() > 0);
- List<String> notStarted = new ArrayList<String>();
- while (!allStarted && (System.currentTimeMillis() - beginMonitor) < defaultTimeout) {
- notStarted = new ArrayList<String>();
- allStarted = true;
- for (int i = 0; i < startedBundles.size(); i++) {
- Bundle bundle = (Bundle) startedBundles.get(i);
- // TODO check behaviour of lazs bundles
- if (bundle.getState() != Bundle.ACTIVE) {
- allStarted = false;
- notStarted.add(bundle.getSymbolicName());
- }
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // silent
- }
- }
- long duration = System.currentTimeMillis() - beginMonitor;
-
- if (!allStarted)
- for (int i = 0; i < notStarted.size(); i++)
- OsgiBootUtils.warn("Bundle '" + notStarted.get(i) + "' not ACTIVE after " + (duration / 1000) + "s");
-
- return allStarted;
- }
-
- /** Waits for a bundle to become active or resolved */
- @Deprecated
- private void waitForBundleResolvedOrActive(long startBegin, Bundle bundle) throws Exception {
- int originalState = bundle.getState();
- if ((originalState == Bundle.RESOLVED) || (originalState == Bundle.ACTIVE))
- return;
-
- String originalStateStr = OsgiBootUtils.stateAsString(originalState);
-
- int currentState = bundle.getState();
- while (!(currentState == Bundle.RESOLVED || currentState == Bundle.ACTIVE)) {
- long now = System.currentTimeMillis();
- if ((now - startBegin) > defaultTimeout * 10)
- throw new Exception("Bundle " + bundle.getSymbolicName() + " was not RESOLVED or ACTIVE after "
- + (now - startBegin) + "ms (originalState=" + originalStateStr + ", currentState="
- + OsgiBootUtils.stateAsString(currentState) + ")");
-
- try {
- Thread.sleep(100l);
- } catch (InterruptedException e) {
- // silent
- }
- currentState = bundle.getState();
- }
- }
-
- /*
- * BUNDLE PATTERNS INSTALLATION
- */
- /**
- * Computes a list of URLs based on Ant-like include/exclude patterns defined by
- * ${argeo.osgi.bundles} with the following format:<br>
- * <code>/base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar</code><br>
- * WARNING: <code>/base/directory;in=*.jar,\</code> at the end of a file,
- * without a new line causes a '.' to be appended with unexpected side effects.
- */
- public List<String> getBundlesUrls() {
- String bundlePatterns = getProperty(PROP_ARGEO_OSGI_BUNDLES);
- return getBundlesUrls(bundlePatterns);
- }
-
- /**
- * Compute a list of URLs to install based on the provided patterns, with
- * default base url
- */
- public List<String> getBundlesUrls(String bundlePatterns) {
- String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL, DEFAULT_BASE_URL);
- return getBundlesUrls(baseUrl, bundlePatterns);
- }
-
- /** Implements the path matching logic */
- public List<String> getBundlesUrls(String baseUrl, String bundlePatterns) {
- List<String> urls = new ArrayList<String>();
- if (bundlePatterns == null)
- return urls;
-
- bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns);
- if (OsgiBootUtils.debug)
- debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns);
-
- StringTokenizer st = new StringTokenizer(bundlePatterns, ",");
- List<BundlesSet> bundlesSets = new ArrayList<BundlesSet>();
- while (st.hasMoreTokens()) {
- String token = st.nextToken();
- if (new File(token).exists()) {
- String url = locationToUrl(baseUrl, token);
- urls.add(url);
- } else
- bundlesSets.add(new BundlesSet(token));
- }
-
- // find included
- List<String> included = new ArrayList<String>();
- PathMatcher matcher = new AntPathMatcher();
- for (int i = 0; i < bundlesSets.size(); i++) {
- BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
- for (int j = 0; j < bundlesSet.getIncludes().size(); j++) {
- String pattern = (String) bundlesSet.getIncludes().get(j);
- match(matcher, included, bundlesSet.getDir(), null, pattern);
- }
- }
-
- // find excluded
- List<String> excluded = new ArrayList<String>();
- for (int i = 0; i < bundlesSets.size(); i++) {
- BundlesSet bundlesSet = (BundlesSet) bundlesSets.get(i);
- for (int j = 0; j < bundlesSet.getExcludes().size(); j++) {
- String pattern = (String) bundlesSet.getExcludes().get(j);
- match(matcher, excluded, bundlesSet.getDir(), null, pattern);
- }
- }
-
- // construct list
- for (int i = 0; i < included.size(); i++) {
- String fullPath = (String) included.get(i);
- if (!excluded.contains(fullPath))
- urls.add(locationToUrl(baseUrl, fullPath));
- }
-
- return urls;
- }
-
- /*
- * DISTRIBUTION JAR INSTALLATION
- */
- public List<String> getDistributionUrls() {
- String distributionUrl = getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL);
- String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL);
- return getDistributionUrls(distributionUrl, baseUrl);
- }
-
- public List<String> getDistributionUrls(String distributionUrl, String baseUrl) {
- List<String> urls = new ArrayList<String>();
- if (distributionUrl == null)
- return urls;
-
- DistributionBundle distributionBundle;
- if (distributionUrl.startsWith("http") || distributionUrl.startsWith("file")) {
- distributionBundle = new DistributionBundle(distributionUrl);
- if (baseUrl != null)
- distributionBundle.setBaseUrl(baseUrl);
- } else {
- // relative url
- if (baseUrl == null) {
- baseUrl = localCache;
- }
-
- if (distributionUrl.contains(":")) {
- // TODO make it safer
- String[] parts = distributionUrl.trim().split(":");
- String[] categoryParts = parts[0].split("\\.");
- String artifactId = parts[1];
- String version = parts[2];
- StringBuilder sb = new StringBuilder();
- for (String categoryPart : categoryParts) {
- sb.append(categoryPart).append('/');
- }
- sb.append(artifactId).append('/');
- sb.append(version).append('/');
- sb.append(artifactId).append('-').append(version).append(".jar");
- distributionUrl = sb.toString();
- }
-
- distributionBundle = new DistributionBundle(baseUrl, distributionUrl, localCache);
- }
- // if (baseUrl != null && !(distributionUrl.startsWith("http") ||
- // distributionUrl.startsWith("file"))) {
- // // relative url
- // distributionBundle = new DistributionBundle(baseUrl, distributionUrl,
- // localCache);
- // } else {
- // distributionBundle = new DistributionBundle(distributionUrl);
- // if (baseUrl != null)
- // distributionBundle.setBaseUrl(baseUrl);
- // }
- distributionBundle.processUrl();
- return distributionBundle.listUrls();
- }
-
- /*
- * HIGH LEVEL UTILITIES
- */
- /** Actually performs the matching logic. */
- protected void match(PathMatcher matcher, List<String> matched, String base, String currentPath, String pattern) {
- if (currentPath == null) {
- // Init
- File baseDir = new File(base.replace('/', File.separatorChar));
- File[] files = baseDir.listFiles();
-
- if (files == null) {
- if (OsgiBootUtils.debug)
- OsgiBootUtils.warn("Base dir " + baseDir + " has no children, exists=" + baseDir.exists()
- + ", isDirectory=" + baseDir.isDirectory());
- return;
- }
-
- for (int i = 0; i < files.length; i++)
- match(matcher, matched, base, files[i].getName(), pattern);
- } else {
- String fullPath = base + '/' + currentPath;
- if (matched.contains(fullPath))
- return;// don't try deeper if already matched
-
- boolean ok = matcher.match(pattern, currentPath);
- // if (debug)
- // debug(currentPath + " " + (ok ? "" : " not ")
- // + " matched with " + pattern);
- if (ok) {
- matched.add(fullPath);
- return;
- } else {
- String newFullPath = relativeToFullPath(base, currentPath);
- File newFile = new File(newFullPath);
- File[] files = newFile.listFiles();
- if (files != null) {
- for (int i = 0; i < files.length; i++) {
- String newCurrentPath = currentPath + '/' + files[i].getName();
- if (files[i].isDirectory()) {
- if (matcher.matchStart(pattern, newCurrentPath)) {
- // recurse only if start matches
- match(matcher, matched, base, newCurrentPath, pattern);
- } else {
- if (OsgiBootUtils.debug)
- debug(newCurrentPath + " does not start match with " + pattern);
-
- }
- } else {
- boolean nonDirectoryOk = matcher.match(pattern, newCurrentPath);
- if (OsgiBootUtils.debug)
- debug(currentPath + " " + (ok ? "" : " not ") + " matched with " + pattern);
- if (nonDirectoryOk)
- matched.add(relativeToFullPath(base, newCurrentPath));
- }
- }
- }
- }
- }
- }
-
- protected void matchFile() {
-
- }
-
- /*
- * LOW LEVEL UTILITIES
- */
- /**
- * The bundles already installed. Key is location (String) , value is a
- * {@link Bundle}
- */
- public Map<String, Bundle> getBundlesByLocation() {
- Map<String, Bundle> installedBundles = new HashMap<String, Bundle>();
- Bundle[] bundles = bundleContext.getBundles();
- for (int i = 0; i < bundles.length; i++) {
- installedBundles.put(bundles[i].getLocation(), bundles[i]);
- }
- return installedBundles;
- }
-
- /**
- * The bundles already installed. Key is symbolic name (String) , value is a
- * {@link Bundle}
- */
- public Map<String, Bundle> getBundlesBySymbolicName() {
- Map<String, Bundle> namedBundles = new HashMap<String, Bundle>();
- Bundle[] bundles = bundleContext.getBundles();
- for (int i = 0; i < bundles.length; i++) {
- namedBundles.put(bundles[i].getSymbolicName(), bundles[i]);
- }
- return namedBundles;
- }
-
- /** Creates an URL from a location */
- protected String locationToUrl(String baseUrl, String location) {
- return baseUrl + location;
- }
-
- /** Transforms a relative path in a full system path. */
- protected String relativeToFullPath(String basePath, String relativePath) {
- return (basePath + '/' + relativePath).replace('/', File.separatorChar);
- }
-
- private void refreshFramework() {
- Bundle systemBundle = bundleContext.getBundle(0);
- FrameworkWiring frameworkWiring = systemBundle.adapt(FrameworkWiring.class);
- frameworkWiring.refreshBundles(null);
- }
-
- /**
- * Gets a property value
- *
- * @return null when defaultValue is ""
- */
- public String getProperty(String name, String defaultValue) {
- String value = bundleContext.getProperty(name);
- if (value == null)
- return defaultValue; // may be null
- else
- return value;
- }
-
- public String getProperty(String name) {
- return getProperty(name, null);
- }
-
- /*
- * BEAN METHODS
- */
-
- public boolean getDebug() {
- return OsgiBootUtils.debug;
- }
-
- // public void setDebug(boolean debug) {
- // this.debug = debug;
- // }
-
- public BundleContext getBundleContext() {
- return bundleContext;
- }
-
- public String getLocalCache() {
- return localCache;
- }
-
- // public void setDefaultTimeout(long defaultTimeout) {
- // this.defaultTimeout = defaultTimeout;
- // }
-
- // public boolean isExcludeSvn() {
- // return excludeSvn;
- // }
- //
- // public void setExcludeSvn(boolean excludeSvn) {
- // this.excludeSvn = excludeSvn;
- // }
-
- /*
- * INTERNAL CLASSES
- */
-
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-public interface OsgiBootConstants {
-
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-
-@SuppressWarnings("deprecation")
-class OsgiBootDiagnostics {
- private final BundleContext bundleContext;
-
- public OsgiBootDiagnostics(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
- /*
- * DIAGNOSTICS
- */
- /** Check unresolved bundles */
- protected void checkUnresolved() {
- // Refresh
- ServiceReference<PackageAdmin> packageAdminRef = bundleContext.getServiceReference(PackageAdmin.class);
- PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(packageAdminRef);
- packageAdmin.resolveBundles(null);
-
- Bundle[] bundles = bundleContext.getBundles();
- List<Bundle> unresolvedBundles = new ArrayList<Bundle>();
- for (int i = 0; i < bundles.length; i++) {
- int bundleState = bundles[i].getState();
- if (!(bundleState == Bundle.ACTIVE || bundleState == Bundle.RESOLVED || bundleState == Bundle.STARTING))
- unresolvedBundles.add(bundles[i]);
- }
-
- if (unresolvedBundles.size() != 0) {
- OsgiBootUtils.warn("Unresolved bundles " + unresolvedBundles);
- }
- }
-
- /** List packages exported twice. */
- public Map<String, Set<String>> findPackagesExportedTwice() {
- ServiceReference<PackageAdmin> paSr = bundleContext.getServiceReference(PackageAdmin.class);
- PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(paSr);
-
- // find packages exported twice
- Bundle[] bundles = bundleContext.getBundles();
- Map<String, Set<String>> exportedPackages = new TreeMap<String, Set<String>>();
- for (int i = 0; i < bundles.length; i++) {
- Bundle bundle = bundles[i];
- ExportedPackage[] pkgs = packageAdmin.getExportedPackages(bundle);
- if (pkgs != null)
- for (int j = 0; j < pkgs.length; j++) {
- String pkgName = pkgs[j].getName();
- if (!exportedPackages.containsKey(pkgName)) {
- exportedPackages.put(pkgName, new TreeSet<String>());
- }
- (exportedPackages.get(pkgName)).add(bundle.getSymbolicName() + "_" + bundle.getVersion());
- }
- }
- Map<String, Set<String>> duplicatePackages = new TreeMap<String, Set<String>>();
- Iterator<String> it = exportedPackages.keySet().iterator();
- while (it.hasNext()) {
- String pkgName = it.next().toString();
- Set<String> bdles = exportedPackages.get(pkgName);
- if (bdles.size() > 1)
- duplicatePackages.put(pkgName, bdles);
- }
- return duplicatePackages;
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-/** OsgiBoot specific exceptions */
-class OsgiBootException extends RuntimeException {
- private static final long serialVersionUID = 2414011711711425353L;
-
- public OsgiBootException() {
- }
-
- public OsgiBootException(String message) {
- super(message);
- }
-
- public OsgiBootException(String message, Throwable e) {
- super(message, e);
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.launch.Framework;
-import org.osgi.framework.launch.FrameworkFactory;
-
-/** Utilities, mostly related to logging. */
-public class OsgiBootUtils {
- /** ISO8601 (as per log4j) and difference to UTC */
- private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,SSS Z");
-
- static boolean debug = System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_DEBUG) == null ? false
- : !System.getProperty(OsgiBoot.PROP_ARGEO_OSGI_BOOT_DEBUG).trim().equals("false");
-
- public static void info(Object obj) {
- System.out.println("# OSGiBOOT # " + dateFormat.format(new Date()) + " # " + obj);
- }
-
- public static void debug(Object obj) {
- if (debug)
- System.out.println("# OSGiBOOT DBG # " + dateFormat.format(new Date()) + " # " + obj);
- }
-
- public static void warn(Object obj) {
- System.out.println("# OSGiBOOT WARN # " + dateFormat.format(new Date()) + " # " + obj);
- }
-
- public static void error(Object obj, Throwable e) {
- System.err.println("# OSGiBOOT ERR # " + dateFormat.format(new Date()) + " # " + obj);
- if (e != null)
- e.printStackTrace();
- }
-
- public static boolean isDebug() {
- return debug;
- }
-
- public static String stateAsString(int state) {
- switch (state) {
- case Bundle.UNINSTALLED:
- return "UNINSTALLED";
- case Bundle.INSTALLED:
- return "INSTALLED";
- case Bundle.RESOLVED:
- return "RESOLVED";
- case Bundle.STARTING:
- return "STARTING";
- case Bundle.ACTIVE:
- return "ACTIVE";
- case Bundle.STOPPING:
- return "STOPPING";
- default:
- return Integer.toString(state);
- }
- }
-
- /**
- * @return ==0: versions are identical, <0: tested version is newer, >0:
- * currentVersion is newer.
- */
- public static int compareVersions(String currentVersion, String testedVersion) {
- List<String> cToks = new ArrayList<String>();
- StringTokenizer cSt = new StringTokenizer(currentVersion, ".");
- while (cSt.hasMoreTokens())
- cToks.add(cSt.nextToken());
- List<String> tToks = new ArrayList<String>();
- StringTokenizer tSt = new StringTokenizer(currentVersion, ".");
- while (tSt.hasMoreTokens())
- tToks.add(tSt.nextToken());
-
- int comp = 0;
- comp: for (int i = 0; i < cToks.size(); i++) {
- if (tToks.size() <= i) {
- // equals until then, tested shorter
- comp = 1;
- break comp;
- }
-
- String c = (String) cToks.get(i);
- String t = (String) tToks.get(i);
-
- try {
- int cInt = Integer.parseInt(c);
- int tInt = Integer.parseInt(t);
- if (cInt == tInt)
- continue comp;
- else {
- comp = (cInt - tInt);
- break comp;
- }
- } catch (NumberFormatException e) {
- if (c.equals(t))
- continue comp;
- else {
- comp = c.compareTo(t);
- break comp;
- }
- }
- }
-
- if (comp == 0 && tToks.size() > cToks.size()) {
- // equals until then, current shorter
- comp = -1;
- }
-
- return comp;
- }
-
- /** Launch an OSGi framework. */
- public static Framework launch(FrameworkFactory frameworkFactory, Map<String, String> configuration) {
- // start OSGi
- Framework framework = frameworkFactory.newFramework(configuration);
- try {
- framework.start();
- } catch (BundleException e) {
- throw new OsgiBootException("Cannot start OSGi framework", e);
- }
- return framework;
- }
-
-}
+++ /dev/null
-package org.argeo.osgi.boot;
-
-import java.lang.reflect.Method;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.eclipse.osgi.launch.EquinoxFactory;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.launch.Framework;
-import org.osgi.framework.launch.FrameworkFactory;
-import org.osgi.util.tracker.BundleTracker;
-import org.osgi.util.tracker.ServiceTracker;
-
-/** OSGi builder, focusing on ease of use for scripting. */
-public class OsgiBuilder {
- private final static String PROP_HTTP_PORT = "org.osgi.service.http.port";
- private final static String PROP_HTTPS_PORT = "org.osgi.service.https.port";
- private final static String PROP_OSGI_CLEAN = "osgi.clean";
-
- private Map<Integer, StartLevel> startLevels = new TreeMap<>();
- private List<String> distributionBundles = new ArrayList<>();
-
- private Map<String, String> configuration = new HashMap<String, String>();
- private Framework framework;
- private String baseUrl = null;
-
- public OsgiBuilder() {
- // configuration.put("osgi.clean", "true");
- configuration.put(OsgiBoot.CONFIGURATION_AREA_PROP, System.getProperty(OsgiBoot.CONFIGURATION_AREA_PROP));
- configuration.put(OsgiBoot.INSTANCE_AREA_PROP, System.getProperty(OsgiBoot.INSTANCE_AREA_PROP));
- configuration.put(PROP_OSGI_CLEAN, System.getProperty(PROP_OSGI_CLEAN));
- }
-
- public Framework launch() {
- // start OSGi
- FrameworkFactory frameworkFactory = new EquinoxFactory();
- framework = frameworkFactory.newFramework(configuration);
- try {
- framework.start();
- } catch (BundleException e) {
- throw new OsgiBootException("Cannot start OSGi framework", e);
- }
-
- BundleContext bc = framework.getBundleContext();
- String osgiData = bc.getProperty(OsgiBoot.INSTANCE_AREA_PROP);
- // String osgiConf = bc.getProperty(OsgiBoot.CONFIGURATION_AREA_PROP);
- String osgiConf = framework.getDataFile("").getAbsolutePath();
- if (OsgiBootUtils.isDebug())
- OsgiBootUtils.debug("OSGi starting - data: " + osgiData + " conf: " + osgiConf);
-
- OsgiBoot osgiBoot = new OsgiBoot(framework.getBundleContext());
- if (distributionBundles.isEmpty()) {
- osgiBoot.getProvisioningManager().install(null);
- } else {
- // install bundles
- for (String distributionBundle : distributionBundles) {
- List<String> bundleUrls = osgiBoot.getDistributionUrls(distributionBundle, baseUrl);
- osgiBoot.installUrls(bundleUrls);
- }
- }
- // start bundles
- osgiBoot.startBundles(startLevelsToProperties());
-
- // if (OsgiBootUtils.isDebug())
- // for (Bundle bundle : bc.getBundles()) {
- // OsgiBootUtils.debug(bundle.getLocation());
- // }
- return framework;
- }
-
- public OsgiBuilder conf(String key, String value) {
- checkNotLaunched();
- configuration.put(key, value);
- return this;
- }
-
- public OsgiBuilder install(String uri) {
- // TODO dynamic install
- checkNotLaunched();
- if (!distributionBundles.contains(uri))
- distributionBundles.add(uri);
- return this;
- }
-
- public OsgiBuilder start(int startLevel, String bundle) {
- // TODO dynamic start
- checkNotLaunched();
- StartLevel sl;
- if (!startLevels.containsKey(startLevel))
- startLevels.put(startLevel, new StartLevel());
- sl = startLevels.get(startLevel);
- sl.add(bundle);
- return this;
- }
-
- public OsgiBuilder waitForServlet(String base) {
- service("(&(objectClass=javax.servlet.Servlet)(osgi.http.whiteboard.servlet.pattern=" + base + "))");
- return this;
- }
-
- public OsgiBuilder waitForBundle(String bundles) {
- List<String> lst = new ArrayList<>();
- Collections.addAll(lst, bundles.split(","));
- BundleTracker<Object> bt = new BundleTracker<Object>(getBc(), Bundle.ACTIVE, null) {
-
- @Override
- public Object addingBundle(Bundle bundle, BundleEvent event) {
- if (lst.contains(bundle.getSymbolicName())) {
- return bundle.getSymbolicName();
- } else {
- return null;
- }
- }
- };
- bt.open();
- while (bt.getTrackingCount() != lst.size()) {
- try {
- Thread.sleep(500l);
- } catch (InterruptedException e) {
- break;
- }
- }
- bt.close();
- return this;
-
- }
-
- public OsgiBuilder main(String clssUri, String[] args) {
-
- // waitForBundle(bundleSymbolicName);
- try {
- URI uri = new URI(clssUri);
- if (!"bundleclass".equals(uri.getScheme()))
- throw new IllegalArgumentException("Unsupported scheme for " + clssUri);
- String bundleSymbolicName = uri.getHost();
- String clss = uri.getPath().substring(1);
- Bundle bundle = null;
- for (Bundle b : getBc().getBundles()) {
- if (bundleSymbolicName.equals(b.getSymbolicName())) {
- bundle = b;
- break;
- }
- }
- if (bundle == null)
- throw new OsgiBootException("Bundle " + bundleSymbolicName + " not found");
- Class<?> c = bundle.loadClass(clss);
- Object[] mainArgs = { args };
- Method mainMethod = c.getMethod("main", String[].class);
- mainMethod.invoke(null, mainArgs);
- } catch (Throwable e) {
- throw new OsgiBootException("Cannot execute " + clssUri, e);
- }
- return this;
- }
-
- public Object service(String service) {
- return service(service, 0);
- }
-
- public Object service(String service, long timeout) {
- ServiceTracker<Object, Object> st;
- if (service.contains("(")) {
- try {
- st = new ServiceTracker<>(getBc(), FrameworkUtil.createFilter(service), null);
- } catch (InvalidSyntaxException e) {
- throw new IllegalArgumentException("Badly formatted filter", e);
- }
- } else {
- st = new ServiceTracker<>(getBc(), service, null);
- }
- st.open();
- try {
- return st.waitForService(timeout);
- } catch (InterruptedException e) {
- OsgiBootUtils.error("Interrupted", e);
- return null;
- } finally {
- st.close();
- }
-
- }
-
- public void shutdown() {
- checkLaunched();
- try {
- framework.stop();
- } catch (BundleException e) {
- e.printStackTrace();
- System.exit(1);
- }
- try {
- framework.waitForStop(10 * 60 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- System.exit(1);
- }
- System.exit(0);
- }
-
- public void setHttpPort(Integer port) {
- checkNotLaunched();
- configuration.put(PROP_HTTP_PORT, Integer.toString(port));
- }
-
- public void setHttpsPort(Integer port) {
- checkNotLaunched();
- configuration.put(PROP_HTTPS_PORT, Integer.toString(port));
- }
-
- public void setClean(boolean clean) {
- checkNotLaunched();
- configuration.put(PROP_OSGI_CLEAN, Boolean.toString(clean));
- }
-
- public Integer getHttpPort() {
- if (!isLaunched()) {
- if (configuration.containsKey(PROP_HTTP_PORT))
- return Integer.parseInt(configuration.get(PROP_HTTP_PORT));
- else
- return -1;
- } else {
- // TODO wait for service?
- ServiceReference<?> sr = getBc().getServiceReference("org.osgi.service.http.HttpService");
- if (sr == null)
- return -1;
- Object port = sr.getProperty("http.port");
- if (port == null)
- return -1;
- return Integer.parseInt(port.toString());
- }
- }
-
- public Integer getHttpsPort() {
- if (!isLaunched()) {
- if (configuration.containsKey(PROP_HTTPS_PORT))
- return Integer.parseInt(configuration.get(PROP_HTTPS_PORT));
- else
- return -1;
- } else {
- // TODO wait for service?
- ServiceReference<?> sr = getBc().getServiceReference("org.osgi.service.http.HttpService");
- if (sr == null)
- return -1;
- Object port = sr.getProperty("https.port");
- if (port == null)
- return -1;
- return Integer.parseInt(port.toString());
- }
- }
-
- public Object spring(String bundle) {
- return service("(&(Bundle-SymbolicName=" + bundle + ")"
- + "(objectClass=org.springframework.context.ApplicationContext))");
- }
-
- //
- // BEAN
- //
-
- public BundleContext getBc() {
- checkLaunched();
- return framework.getBundleContext();
- }
-
- public void setBaseUrl(String baseUrl) {
- this.baseUrl = baseUrl;
- }
-
- //
- // UTILITIES
- //
- private Properties startLevelsToProperties() {
- Properties properties = new Properties();
- for (Integer startLevel : startLevels.keySet()) {
- String property = OsgiBoot.PROP_ARGEO_OSGI_START + "." + startLevel;
- StringBuilder value = new StringBuilder();
- for (String bundle : startLevels.get(startLevel).getBundles()) {
- value.append(bundle);
- value.append(',');
- }
- // TODO remove trailing comma
- properties.put(property, value.toString());
- }
- return properties;
- }
-
- private void checkLaunched() {
- if (!isLaunched())
- throw new OsgiBootException("OSGi runtime is not launched");
- }
-
- private void checkNotLaunched() {
- if (isLaunched())
- throw new OsgiBootException("OSGi runtime already launched");
- }
-
- private boolean isLaunched() {
- return framework != null;
- }
-
- private static class StartLevel {
- private Set<String> bundles = new HashSet<>();
-
- public void add(String bundle) {
- String[] b = bundle.split(",");
- Collections.addAll(bundles, b);
- }
-
- public Set<String> getBundles() {
- return bundles;
- }
- }
-}
+++ /dev/null
-package org.argeo.osgi.boot.equinox;
-
-import java.util.Map;
-
-import org.argeo.osgi.boot.OsgiBootUtils;
-import org.eclipse.osgi.launch.EquinoxFactory;
-import org.osgi.framework.launch.Framework;
-
-/**
- * Utilities with a dependency to the Equinox OSGi runtime or its configuration.
- */
-public class EquinoxUtils {
-
- public static Framework launch(Map<String, String> configuration) {
- return OsgiBootUtils.launch(new EquinoxFactory(), configuration);
- }
-
- /** Singleton. */
- private EquinoxUtils() {
-
- }
-}
+++ /dev/null
-/** Simple Eclipse Equinox initialisation. */
-package org.argeo.osgi.boot.equinox;
\ No newline at end of file
+++ /dev/null
-/*\r
- * Copyright 2002-2007 the original author or authors.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.argeo.osgi.boot.internal.springutil;\r
-\r
-/**\r
- * PathMatcher implementation for Ant-style path patterns.\r
- * Examples are provided below.\r
- *\r
- * <p>Part of this mapping code has been kindly borrowed from\r
- * <a href="http://ant.apache.org">Apache Ant</a>.\r
- *\r
- * <p>The mapping matches URLs using the following rules:<br>\r
- * <ul>\r
- * <li>? matches one character</li>\r
- * <li>* matches zero or more characters</li>\r
- * <li>** matches zero or more 'directories' in a path</li>\r
- * </ul>\r
- *\r
- * <p>Some examples:<br>\r
- * <ul>\r
- * <li><code>com/t?st.jsp</code> - matches <code>com/test.jsp</code> but also\r
- * <code>com/tast.jsp</code> or <code>com/txst.jsp</code></li>\r
- * <li><code>com/*.jsp</code> - matches all <code>.jsp</code> files in the\r
- * <code>com</code> directory</li>\r
- * <li><code>com/**/test.jsp</code> - matches all <code>test.jsp</code>\r
- * files underneath the <code>com</code> path</li>\r
- * <li><code>org/springframework/**/*.jsp</code> - matches all <code>.jsp</code>\r
- * files underneath the <code>org/springframework</code> path</li>\r
- * <li><code>org/**/servlet/bla.jsp</code> - matches\r
- * <code>org/springframework/servlet/bla.jsp</code> but also\r
- * <code>org/springframework/testing/servlet/bla.jsp</code> and\r
- * <code>org/servlet/bla.jsp</code></li>\r
- * </ul>\r
- *\r
- * @author Alef Arendsen\r
- * @author Juergen Hoeller\r
- * @author Rob Harrop\r
- * @since 16.07.2003\r
- */\r
-public class AntPathMatcher implements PathMatcher {\r
-\r
- /** Default path separator: "/" */\r
- public static final String DEFAULT_PATH_SEPARATOR = "/";\r
-\r
- private String pathSeparator = DEFAULT_PATH_SEPARATOR;\r
-\r
-\r
- /**\r
- * Set the path separator to use for pattern parsing.\r
- * Default is "/", as in Ant.\r
- */\r
- public void setPathSeparator(String pathSeparator) {\r
- this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);\r
- }\r
-\r
-\r
- public boolean isPattern(String path) {\r
- return (path.indexOf('*') != -1 || path.indexOf('?') != -1);\r
- }\r
-\r
- public boolean match(String pattern, String path) {\r
- return doMatch(pattern, path, true);\r
- }\r
-\r
- public boolean matchStart(String pattern, String path) {\r
- return doMatch(pattern, path, false);\r
- }\r
-\r
-\r
- /**\r
- * Actually match the given <code>path</code> against the given <code>pattern</code>.\r
- * @param pattern the pattern to match against\r
- * @param path the path String to test\r
- * @param fullMatch whether a full pattern match is required\r
- * (else a pattern match as far as the given base path goes is sufficient)\r
- * @return <code>true</code> if the supplied <code>path</code> matched,\r
- * <code>false</code> if it didn't\r
- */\r
- protected boolean doMatch(String pattern, String path, boolean fullMatch) {\r
- if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {\r
- return false;\r
- }\r
-\r
- String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);\r
- String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator);\r
-\r
- int pattIdxStart = 0;\r
- int pattIdxEnd = pattDirs.length - 1;\r
- int pathIdxStart = 0;\r
- int pathIdxEnd = pathDirs.length - 1;\r
-\r
- // Match all elements up to the first **\r
- while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
- String patDir = pattDirs[pattIdxStart];\r
- if ("**".equals(patDir)) {\r
- break;\r
- }\r
- if (!matchStrings(patDir, pathDirs[pathIdxStart])) {\r
- return false;\r
- }\r
- pattIdxStart++;\r
- pathIdxStart++;\r
- }\r
-\r
- if (pathIdxStart > pathIdxEnd) {\r
- // Path is exhausted, only match if rest of pattern is * or **'s\r
- if (pattIdxStart > pattIdxEnd) {\r
- return (pattern.endsWith(this.pathSeparator) ?\r
- path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator));\r
- }\r
- if (!fullMatch) {\r
- return true;\r
- }\r
- if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") &&\r
- path.endsWith(this.pathSeparator)) {\r
- return true;\r
- }\r
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
- if (!pattDirs[i].equals("**")) {\r
- return false;\r
- }\r
- }\r
- return true;\r
- }\r
- else if (pattIdxStart > pattIdxEnd) {\r
- // String not exhausted, but pattern is. Failure.\r
- return false;\r
- }\r
- else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {\r
- // Path start definitely matches due to "**" part in pattern.\r
- return true;\r
- }\r
-\r
- // up to last '**'\r
- while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
- String patDir = pattDirs[pattIdxEnd];\r
- if (patDir.equals("**")) {\r
- break;\r
- }\r
- if (!matchStrings(patDir, pathDirs[pathIdxEnd])) {\r
- return false;\r
- }\r
- pattIdxEnd--;\r
- pathIdxEnd--;\r
- }\r
- if (pathIdxStart > pathIdxEnd) {\r
- // String is exhausted\r
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
- if (!pattDirs[i].equals("**")) {\r
- return false;\r
- }\r
- }\r
- return true;\r
- }\r
-\r
- while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {\r
- int patIdxTmp = -1;\r
- for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {\r
- if (pattDirs[i].equals("**")) {\r
- patIdxTmp = i;\r
- break;\r
- }\r
- }\r
- if (patIdxTmp == pattIdxStart + 1) {\r
- // '**/**' situation, so skip one\r
- pattIdxStart++;\r
- continue;\r
- }\r
- // Find the pattern between padIdxStart & padIdxTmp in str between\r
- // strIdxStart & strIdxEnd\r
- int patLength = (patIdxTmp - pattIdxStart - 1);\r
- int strLength = (pathIdxEnd - pathIdxStart + 1);\r
- int foundIdx = -1;\r
-\r
- strLoop:\r
- for (int i = 0; i <= strLength - patLength; i++) {\r
- for (int j = 0; j < patLength; j++) {\r
- String subPat = (String) pattDirs[pattIdxStart + j + 1];\r
- String subStr = (String) pathDirs[pathIdxStart + i + j];\r
- if (!matchStrings(subPat, subStr)) {\r
- continue strLoop;\r
- }\r
- }\r
- foundIdx = pathIdxStart + i;\r
- break;\r
- }\r
-\r
- if (foundIdx == -1) {\r
- return false;\r
- }\r
-\r
- pattIdxStart = patIdxTmp;\r
- pathIdxStart = foundIdx + patLength;\r
- }\r
-\r
- for (int i = pattIdxStart; i <= pattIdxEnd; i++) {\r
- if (!pattDirs[i].equals("**")) {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
- }\r
-\r
- /**\r
- * Tests whether or not a string matches against a pattern.\r
- * The pattern may contain two special characters:<br>\r
- * '*' means zero or more characters<br>\r
- * '?' means one and only one character\r
- * @param pattern pattern to match against.\r
- * Must not be <code>null</code>.\r
- * @param str string which must be matched against the pattern.\r
- * Must not be <code>null</code>.\r
- * @return <code>true</code> if the string matches against the\r
- * pattern, or <code>false</code> otherwise.\r
- */\r
- private boolean matchStrings(String pattern, String str) {\r
- char[] patArr = pattern.toCharArray();\r
- char[] strArr = str.toCharArray();\r
- int patIdxStart = 0;\r
- int patIdxEnd = patArr.length - 1;\r
- int strIdxStart = 0;\r
- int strIdxEnd = strArr.length - 1;\r
- char ch;\r
-\r
- boolean containsStar = false;\r
- for (int i = 0; i < patArr.length; i++) {\r
- if (patArr[i] == '*') {\r
- containsStar = true;\r
- break;\r
- }\r
- }\r
-\r
- if (!containsStar) {\r
- // No '*'s, so we make a shortcut\r
- if (patIdxEnd != strIdxEnd) {\r
- return false; // Pattern and string do not have the same size\r
- }\r
- for (int i = 0; i <= patIdxEnd; i++) {\r
- ch = patArr[i];\r
- if (ch != '?') {\r
- if (ch != strArr[i]) {\r
- return false;// Character mismatch\r
- }\r
- }\r
- }\r
- return true; // String matches against pattern\r
- }\r
-\r
-\r
- if (patIdxEnd == 0) {\r
- return true; // Pattern contains only '*', which matches anything\r
- }\r
-\r
- // Process characters before first star\r
- while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {\r
- if (ch != '?') {\r
- if (ch != strArr[strIdxStart]) {\r
- return false;// Character mismatch\r
- }\r
- }\r
- patIdxStart++;\r
- strIdxStart++;\r
- }\r
- if (strIdxStart > strIdxEnd) {\r
- // All characters in the string are used. Check if only '*'s are\r
- // left in the pattern. If so, we succeeded. Otherwise failure.\r
- for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
- if (patArr[i] != '*') {\r
- return false;\r
- }\r
- }\r
- return true;\r
- }\r
-\r
- // Process characters after last star\r
- while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {\r
- if (ch != '?') {\r
- if (ch != strArr[strIdxEnd]) {\r
- return false;// Character mismatch\r
- }\r
- }\r
- patIdxEnd--;\r
- strIdxEnd--;\r
- }\r
- if (strIdxStart > strIdxEnd) {\r
- // All characters in the string are used. Check if only '*'s are\r
- // left in the pattern. If so, we succeeded. Otherwise failure.\r
- for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
- if (patArr[i] != '*') {\r
- return false;\r
- }\r
- }\r
- return true;\r
- }\r
-\r
- // process pattern between stars. padIdxStart and patIdxEnd point\r
- // always to a '*'.\r
- while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {\r
- int patIdxTmp = -1;\r
- for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {\r
- if (patArr[i] == '*') {\r
- patIdxTmp = i;\r
- break;\r
- }\r
- }\r
- if (patIdxTmp == patIdxStart + 1) {\r
- // Two stars next to each other, skip the first one.\r
- patIdxStart++;\r
- continue;\r
- }\r
- // Find the pattern between padIdxStart & padIdxTmp in str between\r
- // strIdxStart & strIdxEnd\r
- int patLength = (patIdxTmp - patIdxStart - 1);\r
- int strLength = (strIdxEnd - strIdxStart + 1);\r
- int foundIdx = -1;\r
- strLoop:\r
- for (int i = 0; i <= strLength - patLength; i++) {\r
- for (int j = 0; j < patLength; j++) {\r
- ch = patArr[patIdxStart + j + 1];\r
- if (ch != '?') {\r
- if (ch != strArr[strIdxStart + i + j]) {\r
- continue strLoop;\r
- }\r
- }\r
- }\r
-\r
- foundIdx = strIdxStart + i;\r
- break;\r
- }\r
-\r
- if (foundIdx == -1) {\r
- return false;\r
- }\r
-\r
- patIdxStart = patIdxTmp;\r
- strIdxStart = foundIdx + patLength;\r
- }\r
-\r
- // All characters in the string are used. Check if only '*'s are left\r
- // in the pattern. If so, we succeeded. Otherwise failure.\r
- for (int i = patIdxStart; i <= patIdxEnd; i++) {\r
- if (patArr[i] != '*') {\r
- return false;\r
- }\r
- }\r
-\r
- return true;\r
- }\r
-\r
- /**\r
- * Given a pattern and a full path, determine the pattern-mapped part.\r
- * <p>For example:\r
- * <ul>\r
- * <li>'<code>/docs/cvs/commit.html</code>' and '<code>/docs/cvs/commit.html</code> to ''</li>\r
- * <li>'<code>/docs/*</code>' and '<code>/docs/cvs/commit</code> to '<code>cvs/commit</code>'</li>\r
- * <li>'<code>/docs/cvs/*.html</code>' and '<code>/docs/cvs/commit.html</code> to '<code>commit.html</code>'</li>\r
- * <li>'<code>/docs/**</code>' and '<code>/docs/cvs/commit</code> to '<code>cvs/commit</code>'</li>\r
- * <li>'<code>/docs/**\/*.html</code>' and '<code>/docs/cvs/commit.html</code> to '<code>cvs/commit.html</code>'</li>\r
- * <li>'<code>/*.html</code>' and '<code>/docs/cvs/commit.html</code> to '<code>docs/cvs/commit.html</code>'</li>\r
- * <li>'<code>*.html</code>' and '<code>/docs/cvs/commit.html</code> to '<code>/docs/cvs/commit.html</code>'</li>\r
- * <li>'<code>*</code>' and '<code>/docs/cvs/commit.html</code> to '<code>/docs/cvs/commit.html</code>'</li>\r
- * </ul>\r
- * <p>Assumes that {@link #match} returns <code>true</code> for '<code>pattern</code>'\r
- * and '<code>path</code>', but does <strong>not</strong> enforce this.\r
- */\r
- public String extractPathWithinPattern(String pattern, String path) {\r
- String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);\r
- String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);\r
-\r
- StringBuffer buffer = new StringBuffer();\r
-\r
- // Add any path parts that have a wildcarded pattern part.\r
- int puts = 0;\r
- for (int i = 0; i < patternParts.length; i++) {\r
- String patternPart = patternParts[i];\r
- if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) {\r
- if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) {\r
- buffer.append(this.pathSeparator);\r
- }\r
- buffer.append(pathParts[i]);\r
- puts++;\r
- }\r
- }\r
-\r
- // Append any trailing path parts.\r
- for (int i = patternParts.length; i < pathParts.length; i++) {\r
- if (puts > 0 || i > 0) {\r
- buffer.append(this.pathSeparator);\r
- }\r
- buffer.append(pathParts[i]);\r
- }\r
-\r
- return buffer.toString();\r
- }\r
-\r
-}\r
+++ /dev/null
-package org.argeo.osgi.boot.internal.springutil;
-
-import java.io.InputStream;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class Bootstrap {
-
- public static void main(String[] args) {
- try {
- String configurationArea = "file:" + System.getProperty("user.dir") + "/state";
- String instanceArea = "file:" + System.getProperty("user.dir") + "/data";
- String log4jUrl = "file:" + System.getProperty("user.dir") + "/log4j.properties";
-
- System.setProperty("org.osgi.service.http.port", "7070");
- System.setProperty("log4j.configuration", log4jUrl);
-
- System.setProperty("osgi.console", "2323");
- Map<String, String> props = new HashMap<String, String>();
- props.put("osgi.clean", "true");
-// props.put("osgi.console", "2323");
- props.put("osgi.configuration.area", configurationArea);
- props.put("osgi.instance.area", instanceArea);
-
- System.setProperty("argeo.osgi.start.2.node",
- "org.eclipse.equinox.console,org.eclipse.equinox.http.servlet,org.eclipse.equinox.ds,"
- + "org.eclipse.equinox.metatype,org.eclipse.equinox.cm,org.eclipse.rap.rwt.osgi");
- System.setProperty("argeo.osgi.start.3.node", "org.argeo.cms");
-
- // URL osgiJar =
- // Bootstrap.class.getClassLoader().getResource("/usr/share/osgi/boot/org.eclipse.org.jar");
- URL osgiJar = new URL(
- "file:///home/mbaudier/dev/git/apache2/argeo-commons/demo/exec/cms-e4-rap/backup/share/osgi/boot/org.eclipse.org.jar");
- URL osgiBootJar = new URL(
- "file:///home/mbaudier/dev/git/apache2/argeo-commons/demo/exec/cms-e4-rap/backup/share/osgi/boot/org.argeo.osgi.boot.jar");
- URL[] jarUrls = { osgiJar };
- try (URLClassLoader urlCl = new URLClassLoader(jarUrls)) {
-
- // Class<?> factoryClass =
- // urlCl.loadClass("/org/eclipse/osgi/launch/EquinoxFactory");
- Class<?> factoryClass = urlCl.loadClass("org.eclipse.osgi.launch.EquinoxFactory");
- Class<?> frameworkClass = urlCl.loadClass("org.osgi.framework.launch.Framework");
- Class<?> bundleContextClass = urlCl.loadClass("org.osgi.framework.BundleContext");
- Class<?> bundleClass = urlCl.loadClass("org.osgi.framework.Bundle");
-
- Object factory = factoryClass.getConstructor().newInstance();
- Method newFrameworkMethod = factoryClass.getMethod("newFramework", Map.class);
- Object framework = newFrameworkMethod.invoke(factory, props);
- Method startFramework = frameworkClass.getMethod("start", new Class[] {});
- startFramework.invoke(framework);
- Method getBundleContext = frameworkClass.getMethod("getBundleContext", new Class[] {});
- Object bundleContext = getBundleContext.invoke(framework);
- Class<?>[] installArgs = { String.class, InputStream.class };
- Method install = bundleContextClass.getMethod("installBundle", installArgs);
- Method startBundle = bundleClass.getMethod("start");
- Method getSymbolicName = bundleClass.getMethod("getSymbolicName");
-
- Path basePath = Paths.get(
- "/home/mbaudier/dev/git/apache2/argeo-commons/demo/exec/cms-e4-rap/backup/share/osgi/boot/");
- List<Object> bundles = new ArrayList<>();
- for (Path p : Files.newDirectoryStream(basePath)) {
- try (InputStream in = Files.newInputStream(p)) {
- Object bundle = install.invoke(bundleContext, "file:" + p, in);
- bundles.add(bundle);
- System.out.println("Installed " + bundle);
- } catch (Exception e) {
- if (!p.getFileName().toString().startsWith("org.eclipse.osgi")) {
- System.err.println(p);
- e.printStackTrace();
- }
- }
- }
-
-// for (Object bundle : bundles) {
-// try {
-// String symbolicName = getSymbolicName.invoke(bundle).toString();
-// startBundle.invoke(bundle);
-// } catch (Exception e) {
-// // TODO Auto-generated catch block
-// e.printStackTrace();
-// }
-// }
-
- Object osgiBootBundle = install.invoke(bundleContext, osgiBootJar.toString(), osgiBootJar.openStream());
- startBundle.invoke(osgiBootBundle);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
-
-}
+++ /dev/null
-/*\r
- * Copyright 2002-2008 the original author or authors.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.argeo.osgi.boot.internal.springutil;\r
-\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Enumeration;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Properties;\r
-\r
-/**\r
- * Miscellaneous collection utility methods.\r
- * Mainly for internal use within the framework.\r
- *\r
- * @author Juergen Hoeller\r
- * @author Rob Harrop\r
- * @since 1.1.3\r
- */\r
-@SuppressWarnings({ "rawtypes", "unchecked" })\r
-public abstract class CollectionUtils {\r
-\r
- /**\r
- * Return <code>true</code> if the supplied Collection is <code>null</code>\r
- * or empty. Otherwise, return <code>false</code>.\r
- * @param collection the Collection to check\r
- * @return whether the given Collection is empty\r
- */\r
- public static boolean isEmpty(Collection collection) {\r
- return (collection == null || collection.isEmpty());\r
- }\r
-\r
- /**\r
- * Return <code>true</code> if the supplied Map is <code>null</code>\r
- * or empty. Otherwise, return <code>false</code>.\r
- * @param map the Map to check\r
- * @return whether the given Map is empty\r
- */\r
- public static boolean isEmpty(Map map) {\r
- return (map == null || map.isEmpty());\r
- }\r
-\r
- /**\r
- * Convert the supplied array into a List. A primitive array gets\r
- * converted into a List of the appropriate wrapper type.\r
- * <p>A <code>null</code> source value will be converted to an\r
- * empty List.\r
- * @param source the (potentially primitive) array\r
- * @return the converted List result\r
- * @see ObjectUtils#toObjectArray(Object)\r
- */\r
- public static List arrayToList(Object source) {\r
- return Arrays.asList(ObjectUtils.toObjectArray(source));\r
- }\r
-\r
- /**\r
- * Merge the given array into the given Collection.\r
- * @param array the array to merge (may be <code>null</code>)\r
- * @param collection the target Collection to merge the array into\r
- */\r
- public static void mergeArrayIntoCollection(Object array, Collection collection) {\r
- if (collection == null) {\r
- throw new IllegalArgumentException("Collection must not be null");\r
- }\r
- Object[] arr = ObjectUtils.toObjectArray(array);\r
- for (int i = 0; i < arr.length; i++) {\r
- collection.add(arr[i]);\r
- }\r
- }\r
-\r
- /**\r
- * Merge the given Properties instance into the given Map,\r
- * copying all properties (key-value pairs) over.\r
- * <p>Uses <code>Properties.propertyNames()</code> to even catch\r
- * default properties linked into the original Properties instance.\r
- * @param props the Properties instance to merge (may be <code>null</code>)\r
- * @param map the target Map to merge the properties into\r
- */\r
- public static void mergePropertiesIntoMap(Properties props, Map map) {\r
- if (map == null) {\r
- throw new IllegalArgumentException("Map must not be null");\r
- }\r
- if (props != null) {\r
- for (Enumeration en = props.propertyNames(); en.hasMoreElements();) {\r
- String key = (String) en.nextElement();\r
- map.put(key, props.getProperty(key));\r
- }\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Check whether the given Iterator contains the given element.\r
- * @param iterator the Iterator to check\r
- * @param element the element to look for\r
- * @return <code>true</code> if found, <code>false</code> else\r
- */\r
- public static boolean contains(Iterator iterator, Object element) {\r
- if (iterator != null) {\r
- while (iterator.hasNext()) {\r
- Object candidate = iterator.next();\r
- if (ObjectUtils.nullSafeEquals(candidate, element)) {\r
- return true;\r
- }\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Check whether the given Enumeration contains the given element.\r
- * @param enumeration the Enumeration to check\r
- * @param element the element to look for\r
- * @return <code>true</code> if found, <code>false</code> else\r
- */\r
- public static boolean contains(Enumeration enumeration, Object element) {\r
- if (enumeration != null) {\r
- while (enumeration.hasMoreElements()) {\r
- Object candidate = enumeration.nextElement();\r
- if (ObjectUtils.nullSafeEquals(candidate, element)) {\r
- return true;\r
- }\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Check whether the given Collection contains the given element instance.\r
- * <p>Enforces the given instance to be present, rather than returning\r
- * <code>true</code> for an equal element as well.\r
- * @param collection the Collection to check\r
- * @param element the element to look for\r
- * @return <code>true</code> if found, <code>false</code> else\r
- */\r
- public static boolean containsInstance(Collection collection, Object element) {\r
- if (collection != null) {\r
- for (Iterator it = collection.iterator(); it.hasNext();) {\r
- Object candidate = it.next();\r
- if (candidate == element) {\r
- return true;\r
- }\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Return <code>true</code> if any element in '<code>candidates</code>' is\r
- * contained in '<code>source</code>'; otherwise returns <code>false</code>.\r
- * @param source the source Collection\r
- * @param candidates the candidates to search for\r
- * @return whether any of the candidates has been found\r
- */\r
- public static boolean containsAny(Collection source, Collection candidates) {\r
- if (isEmpty(source) || isEmpty(candidates)) {\r
- return false;\r
- }\r
- for (Iterator it = candidates.iterator(); it.hasNext();) {\r
- if (source.contains(it.next())) {\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Return the first element in '<code>candidates</code>' that is contained in\r
- * '<code>source</code>'. If no element in '<code>candidates</code>' is present in\r
- * '<code>source</code>' returns <code>null</code>. Iteration order is\r
- * {@link Collection} implementation specific.\r
- * @param source the source Collection\r
- * @param candidates the candidates to search for\r
- * @return the first present object, or <code>null</code> if not found\r
- */\r
- public static Object findFirstMatch(Collection source, Collection candidates) {\r
- if (isEmpty(source) || isEmpty(candidates)) {\r
- return null;\r
- }\r
- for (Iterator it = candidates.iterator(); it.hasNext();) {\r
- Object candidate = it.next();\r
- if (source.contains(candidate)) {\r
- return candidate;\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * Find a single value of the given type in the given Collection.\r
- * @param collection the Collection to search\r
- * @param type the type to look for\r
- * @return a value of the given type found if there is a clear match,\r
- * or <code>null</code> if none or more than one such value found\r
- */\r
- public static Object findValueOfType(Collection collection, Class type) {\r
- if (isEmpty(collection)) {\r
- return null;\r
- }\r
- Object value = null;\r
- for (Iterator it = collection.iterator(); it.hasNext();) {\r
- Object obj = it.next();\r
- if (type == null || type.isInstance(obj)) {\r
- if (value != null) {\r
- // More than one value found... no clear single value.\r
- return null;\r
- }\r
- value = obj;\r
- }\r
- }\r
- return value;\r
- }\r
-\r
- /**\r
- * Find a single value of one of the given types in the given Collection:\r
- * searching the Collection for a value of the first type, then\r
- * searching for a value of the second type, etc.\r
- * @param collection the collection to search\r
- * @param types the types to look for, in prioritized order\r
- * @return a value of one of the given types found if there is a clear match,\r
- * or <code>null</code> if none or more than one such value found\r
- */\r
- public static Object findValueOfType(Collection collection, Class[] types) {\r
- if (isEmpty(collection) || ObjectUtils.isEmpty(types)) {\r
- return null;\r
- }\r
- for (int i = 0; i < types.length; i++) {\r
- Object value = findValueOfType(collection, types[i]);\r
- if (value != null) {\r
- return value;\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * Determine whether the given Collection only contains a single unique object.\r
- * @param collection the Collection to check\r
- * @return <code>true</code> if the collection contains a single reference or\r
- * multiple references to the same instance, <code>false</code> else\r
- */\r
- public static boolean hasUniqueObject(Collection collection) {\r
- if (isEmpty(collection)) {\r
- return false;\r
- }\r
- boolean hasCandidate = false;\r
- Object candidate = null;\r
- for (Iterator it = collection.iterator(); it.hasNext();) {\r
- Object elem = it.next();\r
- if (!hasCandidate) {\r
- hasCandidate = true;\r
- candidate = elem;\r
- }\r
- else if (candidate != elem) {\r
- return false;\r
- }\r
- }\r
- return true;\r
- }\r
-\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2002-2007 the original author or authors.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.argeo.osgi.boot.internal.springutil;\r
-\r
-import java.lang.reflect.Array;\r
-import java.util.Arrays;\r
-\r
-/**\r
- * Miscellaneous object utility methods. Mainly for internal use within the\r
- * framework; consider Jakarta's Commons Lang for a more comprehensive suite\r
- * of object utilities.\r
- *\r
- * @author Juergen Hoeller\r
- * @author Keith Donald\r
- * @author Rod Johnson\r
- * @author Rob Harrop\r
- * @author Alex Ruiz\r
- * @since 19.03.2004\r
- */\r
-@SuppressWarnings({ "rawtypes", "unchecked" })\r
-public abstract class ObjectUtils {\r
-\r
- private static final int INITIAL_HASH = 7;\r
- private static final int MULTIPLIER = 31;\r
-\r
- private static final String EMPTY_STRING = "";\r
- private static final String NULL_STRING = "null";\r
- private static final String ARRAY_START = "{";\r
- private static final String ARRAY_END = "}";\r
- private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;\r
- private static final String ARRAY_ELEMENT_SEPARATOR = ", ";\r
-\r
-\r
- /**\r
- * Return whether the given throwable is a checked exception:\r
- * that is, neither a RuntimeException nor an Error.\r
- * @param ex the throwable to check\r
- * @return whether the throwable is a checked exception\r
- * @see java.lang.Exception\r
- * @see java.lang.RuntimeException\r
- * @see java.lang.Error\r
- */\r
- public static boolean isCheckedException(Throwable ex) {\r
- return !(ex instanceof RuntimeException || ex instanceof Error);\r
- }\r
-\r
- /**\r
- * Check whether the given exception is compatible with the exceptions\r
- * declared in a throws clause.\r
- * @param ex the exception to checked\r
- * @param declaredExceptions the exceptions declared in the throws clause\r
- * @return whether the given exception is compatible\r
- */\r
- public static boolean isCompatibleWithThrowsClause(Throwable ex, Class[] declaredExceptions) {\r
- if (!isCheckedException(ex)) {\r
- return true;\r
- }\r
- if (declaredExceptions != null) {\r
- for (int i = 0; i < declaredExceptions.length; i++) {\r
- if (declaredExceptions[i].isAssignableFrom(ex.getClass())) {\r
- return true;\r
- }\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Return whether the given array is empty: that is, <code>null</code>\r
- * or of zero length.\r
- * @param array the array to check\r
- * @return whether the given array is empty\r
- */\r
- public static boolean isEmpty(Object[] array) {\r
- return (array == null || array.length == 0);\r
- }\r
-\r
- /**\r
- * Check whether the given array contains the given element.\r
- * @param array the array to check (may be <code>null</code>,\r
- * in which case the return value will always be <code>false</code>)\r
- * @param element the element to check for\r
- * @return whether the element has been found in the given array\r
- */\r
- public static boolean containsElement(Object[] array, Object element) {\r
- if (array == null) {\r
- return false;\r
- }\r
- for (int i = 0; i < array.length; i++) {\r
- if (nullSafeEquals(array[i], element)) {\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Append the given Object to the given array, returning a new array\r
- * consisting of the input array contents plus the given Object.\r
- * @param array the array to append to (can be <code>null</code>)\r
- * @param obj the Object to append\r
- * @return the new array (of the same component type; never <code>null</code>)\r
- */\r
- public static Object[] addObjectToArray(Object[] array, Object obj) {\r
- Class compType = Object.class;\r
- if (array != null) {\r
- compType = array.getClass().getComponentType();\r
- }\r
- else if (obj != null) {\r
- compType = obj.getClass();\r
- }\r
- int newArrLength = (array != null ? array.length + 1 : 1);\r
- Object[] newArr = (Object[]) Array.newInstance(compType, newArrLength);\r
- if (array != null) {\r
- System.arraycopy(array, 0, newArr, 0, array.length);\r
- }\r
- newArr[newArr.length - 1] = obj;\r
- return newArr;\r
- }\r
-\r
- /**\r
- * Convert the given array (which may be a primitive array) to an\r
- * object array (if necessary of primitive wrapper objects).\r
- * <p>A <code>null</code> source value will be converted to an\r
- * empty Object array.\r
- * @param source the (potentially primitive) array\r
- * @return the corresponding object array (never <code>null</code>)\r
- * @throws IllegalArgumentException if the parameter is not an array\r
- */\r
- public static Object[] toObjectArray(Object source) {\r
- if (source instanceof Object[]) {\r
- return (Object[]) source;\r
- }\r
- if (source == null) {\r
- return new Object[0];\r
- }\r
- if (!source.getClass().isArray()) {\r
- throw new IllegalArgumentException("Source is not an array: " + source);\r
- }\r
- int length = Array.getLength(source);\r
- if (length == 0) {\r
- return new Object[0];\r
- }\r
- Class wrapperType = Array.get(source, 0).getClass();\r
- Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);\r
- for (int i = 0; i < length; i++) {\r
- newArray[i] = Array.get(source, i);\r
- }\r
- return newArray;\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------\r
- // Convenience methods for content-based equality/hash-code handling\r
- //---------------------------------------------------------------------\r
-\r
- /**\r
- * Determine if the given objects are equal, returning <code>true</code>\r
- * if both are <code>null</code> or <code>false</code> if only one is\r
- * <code>null</code>.\r
- * <p>Compares arrays with <code>Arrays.equals</code>, performing an equality\r
- * check based on the array elements rather than the array reference.\r
- * @param o1 first Object to compare\r
- * @param o2 second Object to compare\r
- * @return whether the given objects are equal\r
- * @see java.util.Arrays#equals\r
- */\r
- public static boolean nullSafeEquals(Object o1, Object o2) {\r
- if (o1 == o2) {\r
- return true;\r
- }\r
- if (o1 == null || o2 == null) {\r
- return false;\r
- }\r
- if (o1.equals(o2)) {\r
- return true;\r
- }\r
- if (o1.getClass().isArray() && o2.getClass().isArray()) {\r
- if (o1 instanceof Object[] && o2 instanceof Object[]) {\r
- return Arrays.equals((Object[]) o1, (Object[]) o2);\r
- }\r
- if (o1 instanceof boolean[] && o2 instanceof boolean[]) {\r
- return Arrays.equals((boolean[]) o1, (boolean[]) o2);\r
- }\r
- if (o1 instanceof byte[] && o2 instanceof byte[]) {\r
- return Arrays.equals((byte[]) o1, (byte[]) o2);\r
- }\r
- if (o1 instanceof char[] && o2 instanceof char[]) {\r
- return Arrays.equals((char[]) o1, (char[]) o2);\r
- }\r
- if (o1 instanceof double[] && o2 instanceof double[]) {\r
- return Arrays.equals((double[]) o1, (double[]) o2);\r
- }\r
- if (o1 instanceof float[] && o2 instanceof float[]) {\r
- return Arrays.equals((float[]) o1, (float[]) o2);\r
- }\r
- if (o1 instanceof int[] && o2 instanceof int[]) {\r
- return Arrays.equals((int[]) o1, (int[]) o2);\r
- }\r
- if (o1 instanceof long[] && o2 instanceof long[]) {\r
- return Arrays.equals((long[]) o1, (long[]) o2);\r
- }\r
- if (o1 instanceof short[] && o2 instanceof short[]) {\r
- return Arrays.equals((short[]) o1, (short[]) o2);\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Return as hash code for the given object; typically the value of\r
- * <code>{@link Object#hashCode()}</code>. If the object is an array,\r
- * this method will delegate to any of the <code>nullSafeHashCode</code>\r
- * methods for arrays in this class. If the object is <code>null</code>,\r
- * this method returns 0.\r
- * @see #nullSafeHashCode(Object[])\r
- * @see #nullSafeHashCode(boolean[])\r
- * @see #nullSafeHashCode(byte[])\r
- * @see #nullSafeHashCode(char[])\r
- * @see #nullSafeHashCode(double[])\r
- * @see #nullSafeHashCode(float[])\r
- * @see #nullSafeHashCode(int[])\r
- * @see #nullSafeHashCode(long[])\r
- * @see #nullSafeHashCode(short[])\r
- */\r
- public static int nullSafeHashCode(Object obj) {\r
- if (obj == null) {\r
- return 0;\r
- }\r
- if (obj.getClass().isArray()) {\r
- if (obj instanceof Object[]) {\r
- return nullSafeHashCode((Object[]) obj);\r
- }\r
- if (obj instanceof boolean[]) {\r
- return nullSafeHashCode((boolean[]) obj);\r
- }\r
- if (obj instanceof byte[]) {\r
- return nullSafeHashCode((byte[]) obj);\r
- }\r
- if (obj instanceof char[]) {\r
- return nullSafeHashCode((char[]) obj);\r
- }\r
- if (obj instanceof double[]) {\r
- return nullSafeHashCode((double[]) obj);\r
- }\r
- if (obj instanceof float[]) {\r
- return nullSafeHashCode((float[]) obj);\r
- }\r
- if (obj instanceof int[]) {\r
- return nullSafeHashCode((int[]) obj);\r
- }\r
- if (obj instanceof long[]) {\r
- return nullSafeHashCode((long[]) obj);\r
- }\r
- if (obj instanceof short[]) {\r
- return nullSafeHashCode((short[]) obj);\r
- }\r
- }\r
- return obj.hashCode();\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(Object[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + nullSafeHashCode(array[i]);\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(boolean[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + hashCode(array[i]);\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(byte[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + array[i];\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(char[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + array[i];\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(double[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + hashCode(array[i]);\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(float[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + hashCode(array[i]);\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(int[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + array[i];\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(long[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + hashCode(array[i]);\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return a hash code based on the contents of the specified array.\r
- * If <code>array</code> is <code>null</code>, this method returns 0.\r
- */\r
- public static int nullSafeHashCode(short[] array) {\r
- if (array == null) {\r
- return 0;\r
- }\r
- int hash = INITIAL_HASH;\r
- int arraySize = array.length;\r
- for (int i = 0; i < arraySize; i++) {\r
- hash = MULTIPLIER * hash + array[i];\r
- }\r
- return hash;\r
- }\r
-\r
- /**\r
- * Return the same value as <code>{@link Boolean#hashCode()}</code>.\r
- * @see Boolean#hashCode()\r
- */\r
- public static int hashCode(boolean bool) {\r
- return bool ? 1231 : 1237;\r
- }\r
-\r
- /**\r
- * Return the same value as <code>{@link Double#hashCode()}</code>.\r
- * @see Double#hashCode()\r
- */\r
- public static int hashCode(double dbl) {\r
- long bits = Double.doubleToLongBits(dbl);\r
- return hashCode(bits);\r
- }\r
-\r
- /**\r
- * Return the same value as <code>{@link Float#hashCode()}</code>.\r
- * @see Float#hashCode()\r
- */\r
- public static int hashCode(float flt) {\r
- return Float.floatToIntBits(flt);\r
- }\r
-\r
- /**\r
- * Return the same value as <code>{@link Long#hashCode()}</code>.\r
- * @see Long#hashCode()\r
- */\r
- public static int hashCode(long lng) {\r
- return (int) (lng ^ (lng >>> 32));\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------\r
- // Convenience methods for toString output\r
- //---------------------------------------------------------------------\r
-\r
- /**\r
- * Return a String representation of an object's overall identity.\r
- * @param obj the object (may be <code>null</code>)\r
- * @return the object's identity as String representation,\r
- * or an empty String if the object was <code>null</code>\r
- */\r
- public static String identityToString(Object obj) {\r
- if (obj == null) {\r
- return EMPTY_STRING;\r
- }\r
- return obj.getClass().getName() + "@" + getIdentityHexString(obj);\r
- }\r
-\r
- /**\r
- * Return a hex String form of an object's identity hash code.\r
- * @param obj the object\r
- * @return the object's identity code in hex notation\r
- */\r
- public static String getIdentityHexString(Object obj) {\r
- return Integer.toHexString(System.identityHashCode(obj));\r
- }\r
-\r
- /**\r
- * Return a content-based String representation if <code>obj</code> is\r
- * not <code>null</code>; otherwise returns an empty String.\r
- * <p>Differs from {@link #nullSafeToString(Object)} in that it returns\r
- * an empty String rather than "null" for a <code>null</code> value.\r
- * @param obj the object to build a display String for\r
- * @return a display String representation of <code>obj</code>\r
- * @see #nullSafeToString(Object)\r
- */\r
- public static String getDisplayString(Object obj) {\r
- if (obj == null) {\r
- return EMPTY_STRING;\r
- }\r
- return nullSafeToString(obj);\r
- }\r
-\r
- /**\r
- * Determine the class name for the given object.\r
- * <p>Returns <code>"null"</code> if <code>obj</code> is <code>null</code>.\r
- * @param obj the object to introspect (may be <code>null</code>)\r
- * @return the corresponding class name\r
- */\r
- public static String nullSafeClassName(Object obj) {\r
- return (obj != null ? obj.getClass().getName() : NULL_STRING);\r
- }\r
-\r
- /**\r
- * Return a String representation of the specified Object.\r
- * <p>Builds a String representation of the contents in case of an array.\r
- * Returns <code>"null"</code> if <code>obj</code> is <code>null</code>.\r
- * @param obj the object to build a String representation for\r
- * @return a String representation of <code>obj</code>\r
- */\r
- public static String nullSafeToString(Object obj) {\r
- if (obj == null) {\r
- return NULL_STRING;\r
- }\r
- if (obj instanceof String) {\r
- return (String) obj;\r
- }\r
- if (obj instanceof Object[]) {\r
- return nullSafeToString((Object[]) obj);\r
- }\r
- if (obj instanceof boolean[]) {\r
- return nullSafeToString((boolean[]) obj);\r
- }\r
- if (obj instanceof byte[]) {\r
- return nullSafeToString((byte[]) obj);\r
- }\r
- if (obj instanceof char[]) {\r
- return nullSafeToString((char[]) obj);\r
- }\r
- if (obj instanceof double[]) {\r
- return nullSafeToString((double[]) obj);\r
- }\r
- if (obj instanceof float[]) {\r
- return nullSafeToString((float[]) obj);\r
- }\r
- if (obj instanceof int[]) {\r
- return nullSafeToString((int[]) obj);\r
- }\r
- if (obj instanceof long[]) {\r
- return nullSafeToString((long[]) obj);\r
- }\r
- if (obj instanceof short[]) {\r
- return nullSafeToString((short[]) obj);\r
- }\r
- String str = obj.toString();\r
- return (str != null ? str : EMPTY_STRING);\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(Object[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
- buffer.append(String.valueOf(array[i]));\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(boolean[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
-\r
- buffer.append(array[i]);\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(byte[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
- buffer.append(array[i]);\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(char[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
- buffer.append("'").append(array[i]).append("'");\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(double[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
-\r
- buffer.append(array[i]);\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(float[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
-\r
- buffer.append(array[i]);\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(int[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
- buffer.append(array[i]);\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(long[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
- buffer.append(array[i]);\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
- /**\r
- * Return a String representation of the contents of the specified array.\r
- * <p>The String representation consists of a list of the array's elements,\r
- * enclosed in curly braces (<code>"{}"</code>). Adjacent elements are separated\r
- * by the characters <code>", "</code> (a comma followed by a space). Returns\r
- * <code>"null"</code> if <code>array</code> is <code>null</code>.\r
- * @param array the array to build a String representation for\r
- * @return a String representation of <code>array</code>\r
- */\r
- public static String nullSafeToString(short[] array) {\r
- if (array == null) {\r
- return NULL_STRING;\r
- }\r
- int length = array.length;\r
- if (length == 0) {\r
- return EMPTY_ARRAY;\r
- }\r
- StringBuffer buffer = new StringBuffer();\r
- for (int i = 0; i < length; i++) {\r
- if (i == 0) {\r
- buffer.append(ARRAY_START);\r
- }\r
- else {\r
- buffer.append(ARRAY_ELEMENT_SEPARATOR);\r
- }\r
- buffer.append(array[i]);\r
- }\r
- buffer.append(ARRAY_END);\r
- return buffer.toString();\r
- }\r
-\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2002-2007 the original author or authors.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.argeo.osgi.boot.internal.springutil;\r
-\r
-/**\r
- * Strategy interface for <code>String</code>-based path matching.\r
- * \r
- * <p>Used by {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver},\r
- * {@link org.springframework.web.servlet.handler.AbstractUrlHandlerMapping},\r
- * {@link org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver},\r
- * and {@link org.springframework.web.servlet.mvc.WebContentInterceptor}.\r
- *\r
- * <p>The default implementation is {@link AntPathMatcher}, supporting the\r
- * Ant-style pattern syntax.\r
- *\r
- * @author Juergen Hoeller\r
- * @since 1.2\r
- * @see AntPathMatcher\r
- */\r
-public interface PathMatcher {\r
-\r
- /**\r
- * Does the given <code>path</code> represent a pattern that can be matched\r
- * by an implementation of this interface?\r
- * <p>If the return value is <code>false</code>, then the {@link #match}\r
- * method does not have to be used because direct equality comparisons\r
- * on the static path Strings will lead to the same result.\r
- * @param path the path String to check\r
- * @return <code>true</code> if the given <code>path</code> represents a pattern\r
- */\r
- boolean isPattern(String path);\r
-\r
- /**\r
- * Match the given <code>path</code> against the given <code>pattern</code>,\r
- * according to this PathMatcher's matching strategy.\r
- * @param pattern the pattern to match against\r
- * @param path the path String to test\r
- * @return <code>true</code> if the supplied <code>path</code> matched,\r
- * <code>false</code> if it didn't\r
- */\r
- boolean match(String pattern, String path);\r
-\r
- /**\r
- * Match the given <code>path</code> against the corresponding part of the given\r
- * <code>pattern</code>, according to this PathMatcher's matching strategy.\r
- * <p>Determines whether the pattern at least matches as far as the given base\r
- * path goes, assuming that a full path may then match as well.\r
- * @param pattern the pattern to match against\r
- * @param path the path String to test\r
- * @return <code>true</code> if the supplied <code>path</code> matched,\r
- * <code>false</code> if it didn't\r
- */\r
- boolean matchStart(String pattern, String path);\r
-\r
- /**\r
- * Given a pattern and a full path, determine the pattern-mapped part.\r
- * <p>This method is supposed to find out which part of the path is matched\r
- * dynamically through an actual pattern, that is, it strips off a statically\r
- * defined leading path from the given full path, returning only the actually\r
- * pattern-matched part of the path.\r
- * <p>For example: For "myroot/*.html" as pattern and "myroot/myfile.html"\r
- * as full path, this method should return "myfile.html". The detailed\r
- * determination rules are specified to this PathMatcher's matching strategy.\r
- * <p>A simple implementation may return the given full path as-is in case\r
- * of an actual pattern, and the empty String in case of the pattern not\r
- * containing any dynamic parts (i.e. the <code>pattern</code> parameter being\r
- * a static path that wouldn't qualify as an actual {@link #isPattern pattern}).\r
- * A sophisticated implementation will differentiate between the static parts\r
- * and the dynamic parts of the given path pattern.\r
- * @param pattern the path pattern\r
- * @param path the full path to introspect\r
- * @return the pattern-mapped part of the given <code>path</code>\r
- * (never <code>null</code>)\r
- */\r
- String extractPathWithinPattern(String pattern, String path);\r
-\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2002-2008 the original author or authors.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.argeo.osgi.boot.internal.springutil;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Arrays;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.Enumeration;\r
-import java.util.Iterator;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Locale;\r
-import java.util.Properties;\r
-import java.util.Set;\r
-import java.util.StringTokenizer;\r
-import java.util.TreeSet;\r
-\r
-/**\r
- * Miscellaneous {@link String} utility methods.\r
- *\r
- * <p>Mainly for internal use within the framework; consider\r
- * <a href="http://jakarta.apache.org/commons/lang/">Jakarta's Commons Lang</a>\r
- * for a more comprehensive suite of String utilities.\r
- *\r
- * <p>This class delivers some simple functionality that should really\r
- * be provided by the core Java <code>String</code> and {@link StringBuffer}\r
- * classes, such as the ability to {@link #replace} all occurrences of a given\r
- * substring in a target string. It also provides easy-to-use methods to convert\r
- * between delimited strings, such as CSV strings, and collections and arrays.\r
- *\r
- * @author Rod Johnson\r
- * @author Juergen Hoeller\r
- * @author Keith Donald\r
- * @author Rob Harrop\r
- * @author Rick Evans\r
- * @since 16 April 2001\r
- */\r
-@SuppressWarnings({ "rawtypes", "unchecked" })\r
-public abstract class StringUtils {\r
-\r
- private static final String FOLDER_SEPARATOR = "/";\r
-\r
- private static final String WINDOWS_FOLDER_SEPARATOR = "\\";\r
-\r
- private static final String TOP_PATH = "..";\r
-\r
- private static final String CURRENT_PATH = ".";\r
-\r
- private static final char EXTENSION_SEPARATOR = '.';\r
-\r
-\r
- //---------------------------------------------------------------------\r
- // General convenience methods for working with Strings\r
- //---------------------------------------------------------------------\r
-\r
- /**\r
- * Check that the given CharSequence is neither <code>null</code> nor of length 0.\r
- * Note: Will return <code>true</code> for a CharSequence that purely consists of whitespace.\r
- * <p><pre>\r
- * StringUtils.hasLength(null) = false\r
- * StringUtils.hasLength("") = false\r
- * StringUtils.hasLength(" ") = true\r
- * StringUtils.hasLength("Hello") = true\r
- * </pre>\r
- * @param str the CharSequence to check (may be <code>null</code>)\r
- * @return <code>true</code> if the CharSequence is not null and has length\r
- * @see #hasText(String)\r
- */\r
- public static boolean hasLength(CharSequence str) {\r
- return (str != null && str.length() > 0);\r
- }\r
-\r
- /**\r
- * Check that the given String is neither <code>null</code> nor of length 0.\r
- * Note: Will return <code>true</code> for a String that purely consists of whitespace.\r
- * @param str the String to check (may be <code>null</code>)\r
- * @return <code>true</code> if the String is not null and has length\r
- * @see #hasLength(CharSequence)\r
- */\r
- public static boolean hasLength(String str) {\r
- return hasLength((CharSequence) str);\r
- }\r
-\r
- /**\r
- * Check whether the given CharSequence has actual text.\r
- * More specifically, returns <code>true</code> if the string not <code>null</code>,\r
- * its length is greater than 0, and it contains at least one non-whitespace character.\r
- * <p><pre>\r
- * StringUtils.hasText(null) = false\r
- * StringUtils.hasText("") = false\r
- * StringUtils.hasText(" ") = false\r
- * StringUtils.hasText("12345") = true\r
- * StringUtils.hasText(" 12345 ") = true\r
- * </pre>\r
- * @param str the CharSequence to check (may be <code>null</code>)\r
- * @return <code>true</code> if the CharSequence is not <code>null</code>,\r
- * its length is greater than 0, and it does not contain whitespace only\r
- * @see java.lang.Character#isWhitespace\r
- */\r
- public static boolean hasText(CharSequence str) {\r
- if (!hasLength(str)) {\r
- return false;\r
- }\r
- int strLen = str.length();\r
- for (int i = 0; i < strLen; i++) {\r
- if (!Character.isWhitespace(str.charAt(i))) {\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Check whether the given String has actual text.\r
- * More specifically, returns <code>true</code> if the string not <code>null</code>,\r
- * its length is greater than 0, and it contains at least one non-whitespace character.\r
- * @param str the String to check (may be <code>null</code>)\r
- * @return <code>true</code> if the String is not <code>null</code>, its length is\r
- * greater than 0, and it does not contain whitespace only\r
- * @see #hasText(CharSequence)\r
- */\r
- public static boolean hasText(String str) {\r
- return hasText((CharSequence) str);\r
- }\r
-\r
- /**\r
- * Check whether the given CharSequence contains any whitespace characters.\r
- * @param str the CharSequence to check (may be <code>null</code>)\r
- * @return <code>true</code> if the CharSequence is not empty and\r
- * contains at least 1 whitespace character\r
- * @see java.lang.Character#isWhitespace\r
- */\r
- public static boolean containsWhitespace(CharSequence str) {\r
- if (!hasLength(str)) {\r
- return false;\r
- }\r
- int strLen = str.length();\r
- for (int i = 0; i < strLen; i++) {\r
- if (Character.isWhitespace(str.charAt(i))) {\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- /**\r
- * Check whether the given String contains any whitespace characters.\r
- * @param str the String to check (may be <code>null</code>)\r
- * @return <code>true</code> if the String is not empty and\r
- * contains at least 1 whitespace character\r
- * @see #containsWhitespace(CharSequence)\r
- */\r
- public static boolean containsWhitespace(String str) {\r
- return containsWhitespace((CharSequence) str);\r
- }\r
-\r
- /**\r
- * Trim leading and trailing whitespace from the given String.\r
- * @param str the String to check\r
- * @return the trimmed String\r
- * @see java.lang.Character#isWhitespace\r
- */\r
- public static String trimWhitespace(String str) {\r
- if (!hasLength(str)) {\r
- return str;\r
- }\r
- StringBuffer buf = new StringBuffer(str);\r
- while (buf.length() > 0 && Character.isWhitespace(buf.charAt(0))) {\r
- buf.deleteCharAt(0);\r
- }\r
- while (buf.length() > 0 && Character.isWhitespace(buf.charAt(buf.length() - 1))) {\r
- buf.deleteCharAt(buf.length() - 1);\r
- }\r
- return buf.toString();\r
- }\r
-\r
- /**\r
- * Trim <i>all</i> whitespace from the given String:\r
- * leading, trailing, and inbetween characters.\r
- * @param str the String to check\r
- * @return the trimmed String\r
- * @see java.lang.Character#isWhitespace\r
- */\r
- public static String trimAllWhitespace(String str) {\r
- if (!hasLength(str)) {\r
- return str;\r
- }\r
- StringBuffer buf = new StringBuffer(str);\r
- int index = 0;\r
- while (buf.length() > index) {\r
- if (Character.isWhitespace(buf.charAt(index))) {\r
- buf.deleteCharAt(index);\r
- }\r
- else {\r
- index++;\r
- }\r
- }\r
- return buf.toString();\r
- }\r
-\r
- /**\r
- * Trim leading whitespace from the given String.\r
- * @param str the String to check\r
- * @return the trimmed String\r
- * @see java.lang.Character#isWhitespace\r
- */\r
- public static String trimLeadingWhitespace(String str) {\r
- if (!hasLength(str)) {\r
- return str;\r
- }\r
- StringBuffer buf = new StringBuffer(str);\r
- while (buf.length() > 0 && Character.isWhitespace(buf.charAt(0))) {\r
- buf.deleteCharAt(0);\r
- }\r
- return buf.toString();\r
- }\r
-\r
- /**\r
- * Trim trailing whitespace from the given String.\r
- * @param str the String to check\r
- * @return the trimmed String\r
- * @see java.lang.Character#isWhitespace\r
- */\r
- public static String trimTrailingWhitespace(String str) {\r
- if (!hasLength(str)) {\r
- return str;\r
- }\r
- StringBuffer buf = new StringBuffer(str);\r
- while (buf.length() > 0 && Character.isWhitespace(buf.charAt(buf.length() - 1))) {\r
- buf.deleteCharAt(buf.length() - 1);\r
- }\r
- return buf.toString();\r
- }\r
-\r
- /**\r
- * Trim all occurences of the supplied leading character from the given String.\r
- * @param str the String to check\r
- * @param leadingCharacter the leading character to be trimmed\r
- * @return the trimmed String\r
- */\r
- public static String trimLeadingCharacter(String str, char leadingCharacter) {\r
- if (!hasLength(str)) {\r
- return str;\r
- }\r
- StringBuffer buf = new StringBuffer(str);\r
- while (buf.length() > 0 && buf.charAt(0) == leadingCharacter) {\r
- buf.deleteCharAt(0);\r
- }\r
- return buf.toString();\r
- }\r
-\r
- /**\r
- * Trim all occurences of the supplied trailing character from the given String.\r
- * @param str the String to check\r
- * @param trailingCharacter the trailing character to be trimmed\r
- * @return the trimmed String\r
- */\r
- public static String trimTrailingCharacter(String str, char trailingCharacter) {\r
- if (!hasLength(str)) {\r
- return str;\r
- }\r
- StringBuffer buf = new StringBuffer(str);\r
- while (buf.length() > 0 && buf.charAt(buf.length() - 1) == trailingCharacter) {\r
- buf.deleteCharAt(buf.length() - 1);\r
- }\r
- return buf.toString();\r
- }\r
-\r
-\r
- /**\r
- * Test if the given String starts with the specified prefix,\r
- * ignoring upper/lower case.\r
- * @param str the String to check\r
- * @param prefix the prefix to look for\r
- * @see java.lang.String#startsWith\r
- */\r
- public static boolean startsWithIgnoreCase(String str, String prefix) {\r
- if (str == null || prefix == null) {\r
- return false;\r
- }\r
- if (str.startsWith(prefix)) {\r
- return true;\r
- }\r
- if (str.length() < prefix.length()) {\r
- return false;\r
- }\r
- String lcStr = str.substring(0, prefix.length()).toLowerCase();\r
- String lcPrefix = prefix.toLowerCase();\r
- return lcStr.equals(lcPrefix);\r
- }\r
-\r
- /**\r
- * Test if the given String ends with the specified suffix,\r
- * ignoring upper/lower case.\r
- * @param str the String to check\r
- * @param suffix the suffix to look for\r
- * @see java.lang.String#endsWith\r
- */\r
- public static boolean endsWithIgnoreCase(String str, String suffix) {\r
- if (str == null || suffix == null) {\r
- return false;\r
- }\r
- if (str.endsWith(suffix)) {\r
- return true;\r
- }\r
- if (str.length() < suffix.length()) {\r
- return false;\r
- }\r
-\r
- String lcStr = str.substring(str.length() - suffix.length()).toLowerCase();\r
- String lcSuffix = suffix.toLowerCase();\r
- return lcStr.equals(lcSuffix);\r
- }\r
-\r
- /**\r
- * Test whether the given string matches the given substring\r
- * at the given index.\r
- * @param str the original string (or StringBuffer)\r
- * @param index the index in the original string to start matching against\r
- * @param substring the substring to match at the given index\r
- */\r
- public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {\r
- for (int j = 0; j < substring.length(); j++) {\r
- int i = index + j;\r
- if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {\r
- return false;\r
- }\r
- }\r
- return true;\r
- }\r
-\r
- /**\r
- * Count the occurrences of the substring in string s.\r
- * @param str string to search in. Return 0 if this is null.\r
- * @param sub string to search for. Return 0 if this is null.\r
- */\r
- public static int countOccurrencesOf(String str, String sub) {\r
- if (str == null || sub == null || str.length() == 0 || sub.length() == 0) {\r
- return 0;\r
- }\r
- int count = 0, pos = 0, idx = 0;\r
- while ((idx = str.indexOf(sub, pos)) != -1) {\r
- ++count;\r
- pos = idx + sub.length();\r
- }\r
- return count;\r
- }\r
-\r
- /**\r
- * Replace all occurences of a substring within a string with\r
- * another string.\r
- * @param inString String to examine\r
- * @param oldPattern String to replace\r
- * @param newPattern String to insert\r
- * @return a String with the replacements\r
- */\r
- public static String replace(String inString, String oldPattern, String newPattern) {\r
- if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {\r
- return inString;\r
- }\r
- StringBuffer sbuf = new StringBuffer();\r
- // output StringBuffer we'll build up\r
- int pos = 0; // our position in the old string\r
- int index = inString.indexOf(oldPattern);\r
- // the index of an occurrence we've found, or -1\r
- int patLen = oldPattern.length();\r
- while (index >= 0) {\r
- sbuf.append(inString.substring(pos, index));\r
- sbuf.append(newPattern);\r
- pos = index + patLen;\r
- index = inString.indexOf(oldPattern, pos);\r
- }\r
- sbuf.append(inString.substring(pos));\r
- // remember to append any characters to the right of a match\r
- return sbuf.toString();\r
- }\r
-\r
- /**\r
- * Delete all occurrences of the given substring.\r
- * @param inString the original String\r
- * @param pattern the pattern to delete all occurrences of\r
- * @return the resulting String\r
- */\r
- public static String delete(String inString, String pattern) {\r
- return replace(inString, pattern, "");\r
- }\r
-\r
- /**\r
- * Delete any character in a given String.\r
- * @param inString the original String\r
- * @param charsToDelete a set of characters to delete.\r
- * E.g. "az\n" will delete 'a's, 'z's and new lines.\r
- * @return the resulting String\r
- */\r
- public static String deleteAny(String inString, String charsToDelete) {\r
- if (!hasLength(inString) || !hasLength(charsToDelete)) {\r
- return inString;\r
- }\r
- StringBuffer out = new StringBuffer();\r
- for (int i = 0; i < inString.length(); i++) {\r
- char c = inString.charAt(i);\r
- if (charsToDelete.indexOf(c) == -1) {\r
- out.append(c);\r
- }\r
- }\r
- return out.toString();\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------\r
- // Convenience methods for working with formatted Strings\r
- //---------------------------------------------------------------------\r
-\r
- /**\r
- * Quote the given String with single quotes.\r
- * @param str the input String (e.g. "myString")\r
- * @return the quoted String (e.g. "'myString'"),\r
- * or <code>null</code> if the input was <code>null</code>\r
- */\r
- public static String quote(String str) {\r
- return (str != null ? "'" + str + "'" : null);\r
- }\r
-\r
- /**\r
- * Turn the given Object into a String with single quotes\r
- * if it is a String; keeping the Object as-is else.\r
- * @param obj the input Object (e.g. "myString")\r
- * @return the quoted String (e.g. "'myString'"),\r
- * or the input object as-is if not a String\r
- */\r
- public static Object quoteIfString(Object obj) {\r
- return (obj instanceof String ? quote((String) obj) : obj);\r
- }\r
-\r
- /**\r
- * Unqualify a string qualified by a '.' dot character. For example,\r
- * "this.name.is.qualified", returns "qualified".\r
- * @param qualifiedName the qualified name\r
- */\r
- public static String unqualify(String qualifiedName) {\r
- return unqualify(qualifiedName, '.');\r
- }\r
-\r
- /**\r
- * Unqualify a string qualified by a separator character. For example,\r
- * "this:name:is:qualified" returns "qualified" if using a ':' separator.\r
- * @param qualifiedName the qualified name\r
- * @param separator the separator\r
- */\r
- public static String unqualify(String qualifiedName, char separator) {\r
- return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);\r
- }\r
-\r
- /**\r
- * Capitalize a <code>String</code>, changing the first letter to\r
- * upper case as per {@link Character#toUpperCase(char)}.\r
- * No other letters are changed.\r
- * @param str the String to capitalize, may be <code>null</code>\r
- * @return the capitalized String, <code>null</code> if null\r
- */\r
- public static String capitalize(String str) {\r
- return changeFirstCharacterCase(str, true);\r
- }\r
-\r
- /**\r
- * Uncapitalize a <code>String</code>, changing the first letter to\r
- * lower case as per {@link Character#toLowerCase(char)}.\r
- * No other letters are changed.\r
- * @param str the String to uncapitalize, may be <code>null</code>\r
- * @return the uncapitalized String, <code>null</code> if null\r
- */\r
- public static String uncapitalize(String str) {\r
- return changeFirstCharacterCase(str, false);\r
- }\r
-\r
- private static String changeFirstCharacterCase(String str, boolean capitalize) {\r
- if (str == null || str.length() == 0) {\r
- return str;\r
- }\r
- StringBuffer buf = new StringBuffer(str.length());\r
- if (capitalize) {\r
- buf.append(Character.toUpperCase(str.charAt(0)));\r
- }\r
- else {\r
- buf.append(Character.toLowerCase(str.charAt(0)));\r
- }\r
- buf.append(str.substring(1));\r
- return buf.toString();\r
- }\r
-\r
- /**\r
- * Extract the filename from the given path,\r
- * e.g. "mypath/myfile.txt" to "myfile.txt".\r
- * @param path the file path (may be <code>null</code>)\r
- * @return the extracted filename, or <code>null</code> if none\r
- */\r
- public static String getFilename(String path) {\r
- if (path == null) {\r
- return null;\r
- }\r
- int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);\r
- return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);\r
- }\r
-\r
- /**\r
- * Extract the filename extension from the given path,\r
- * e.g. "mypath/myfile.txt" to "txt".\r
- * @param path the file path (may be <code>null</code>)\r
- * @return the extracted filename extension, or <code>null</code> if none\r
- */\r
- public static String getFilenameExtension(String path) {\r
- if (path == null) {\r
- return null;\r
- }\r
- int sepIndex = path.lastIndexOf(EXTENSION_SEPARATOR);\r
- return (sepIndex != -1 ? path.substring(sepIndex + 1) : null);\r
- }\r
-\r
- /**\r
- * Strip the filename extension from the given path,\r
- * e.g. "mypath/myfile.txt" to "mypath/myfile".\r
- * @param path the file path (may be <code>null</code>)\r
- * @return the path with stripped filename extension,\r
- * or <code>null</code> if none\r
- */\r
- public static String stripFilenameExtension(String path) {\r
- if (path == null) {\r
- return null;\r
- }\r
- int sepIndex = path.lastIndexOf(EXTENSION_SEPARATOR);\r
- return (sepIndex != -1 ? path.substring(0, sepIndex) : path);\r
- }\r
-\r
- /**\r
- * Apply the given relative path to the given path,\r
- * assuming standard Java folder separation (i.e. "/" separators);\r
- * @param path the path to start from (usually a full file path)\r
- * @param relativePath the relative path to apply\r
- * (relative to the full file path above)\r
- * @return the full file path that results from applying the relative path\r
- */\r
- public static String applyRelativePath(String path, String relativePath) {\r
- int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);\r
- if (separatorIndex != -1) {\r
- String newPath = path.substring(0, separatorIndex);\r
- if (!relativePath.startsWith(FOLDER_SEPARATOR)) {\r
- newPath += FOLDER_SEPARATOR;\r
- }\r
- return newPath + relativePath;\r
- }\r
- else {\r
- return relativePath;\r
- }\r
- }\r
-\r
- /**\r
- * Normalize the path by suppressing sequences like "path/.." and\r
- * inner simple dots.\r
- * <p>The result is convenient for path comparison. For other uses,\r
- * notice that Windows separators ("\") are replaced by simple slashes.\r
- * @param path the original path\r
- * @return the normalized path\r
- */\r
- public static String cleanPath(String path) {\r
- if (path == null) {\r
- return null;\r
- }\r
- String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);\r
-\r
- // Strip prefix from path to analyze, to not treat it as part of the\r
- // first path element. This is necessary to correctly parse paths like\r
- // "file:core/../core/io/Resource.class", where the ".." should just\r
- // strip the first "core" directory while keeping the "file:" prefix.\r
- int prefixIndex = pathToUse.indexOf(":");\r
- String prefix = "";\r
- if (prefixIndex != -1) {\r
- prefix = pathToUse.substring(0, prefixIndex + 1);\r
- pathToUse = pathToUse.substring(prefixIndex + 1);\r
- }\r
- if (pathToUse.startsWith(FOLDER_SEPARATOR)) {\r
- prefix = prefix + FOLDER_SEPARATOR;\r
- pathToUse = pathToUse.substring(1);\r
- }\r
-\r
- String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);\r
- List pathElements = new LinkedList();\r
- int tops = 0;\r
-\r
- for (int i = pathArray.length - 1; i >= 0; i--) {\r
- String element = pathArray[i];\r
- if (CURRENT_PATH.equals(element)) {\r
- // Points to current directory - drop it.\r
- }\r
- else if (TOP_PATH.equals(element)) {\r
- // Registering top path found.\r
- tops++;\r
- }\r
- else {\r
- if (tops > 0) {\r
- // Merging path element with element corresponding to top path.\r
- tops--;\r
- }\r
- else {\r
- // Normal path element found.\r
- pathElements.add(0, element);\r
- }\r
- }\r
- }\r
-\r
- // Remaining top paths need to be retained.\r
- for (int i = 0; i < tops; i++) {\r
- pathElements.add(0, TOP_PATH);\r
- }\r
-\r
- return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);\r
- }\r
-\r
- /**\r
- * Compare two paths after normalization of them.\r
- * @param path1 first path for comparison\r
- * @param path2 second path for comparison\r
- * @return whether the two paths are equivalent after normalization\r
- */\r
- public static boolean pathEquals(String path1, String path2) {\r
- return cleanPath(path1).equals(cleanPath(path2));\r
- }\r
-\r
- /**\r
- * Parse the given <code>localeString</code> into a {@link Locale}.\r
- * <p>This is the inverse operation of {@link Locale#toString Locale's toString}.\r
- * @param localeString the locale string, following <code>Locale's</code>\r
- * <code>toString()</code> format ("en", "en_UK", etc);\r
- * also accepts spaces as separators, as an alternative to underscores\r
- * @return a corresponding <code>Locale</code> instance\r
- */\r
- public static Locale parseLocaleString(String localeString) {\r
- String[] parts = tokenizeToStringArray(localeString, "_ ", false, false);\r
- String language = (parts.length > 0 ? parts[0] : "");\r
- String country = (parts.length > 1 ? parts[1] : "");\r
- String variant = "";\r
- if (parts.length >= 2) {\r
- // There is definitely a variant, and it is everything after the country\r
- // code sans the separator between the country code and the variant.\r
- int endIndexOfCountryCode = localeString.indexOf(country) + country.length();\r
- // Strip off any leading '_' and whitespace, what's left is the variant.\r
- variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode));\r
- if (variant.startsWith("_")) {\r
- variant = trimLeadingCharacter(variant, '_');\r
- }\r
- }\r
- return (language.length() > 0 ? new Locale(language, country, variant) : null);\r
- }\r
-\r
- /**\r
- * Determine the RFC 3066 compliant language tag,\r
- * as used for the HTTP "Accept-Language" header.\r
- * @param locale the Locale to transform to a language tag\r
- * @return the RFC 3066 compliant language tag as String\r
- */\r
- public static String toLanguageTag(Locale locale) {\r
- return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : "");\r
- }\r
-\r
-\r
- //---------------------------------------------------------------------\r
- // Convenience methods for working with String arrays\r
- //---------------------------------------------------------------------\r
-\r
- /**\r
- * Append the given String to the given String array, returning a new array\r
- * consisting of the input array contents plus the given String.\r
- * @param array the array to append to (can be <code>null</code>)\r
- * @param str the String to append\r
- * @return the new array (never <code>null</code>)\r
- */\r
- public static String[] addStringToArray(String[] array, String str) {\r
- if (ObjectUtils.isEmpty(array)) {\r
- return new String[] {str};\r
- }\r
- String[] newArr = new String[array.length + 1];\r
- System.arraycopy(array, 0, newArr, 0, array.length);\r
- newArr[array.length] = str;\r
- return newArr;\r
- }\r
-\r
- /**\r
- * Concatenate the given String arrays into one,\r
- * with overlapping array elements included twice.\r
- * <p>The order of elements in the original arrays is preserved.\r
- * @param array1 the first array (can be <code>null</code>)\r
- * @param array2 the second array (can be <code>null</code>)\r
- * @return the new array (<code>null</code> if both given arrays were <code>null</code>)\r
- */\r
- public static String[] concatenateStringArrays(String[] array1, String[] array2) {\r
- if (ObjectUtils.isEmpty(array1)) {\r
- return array2;\r
- }\r
- if (ObjectUtils.isEmpty(array2)) {\r
- return array1;\r
- }\r
- String[] newArr = new String[array1.length + array2.length];\r
- System.arraycopy(array1, 0, newArr, 0, array1.length);\r
- System.arraycopy(array2, 0, newArr, array1.length, array2.length);\r
- return newArr;\r
- }\r
-\r
- /**\r
- * Merge the given String arrays into one, with overlapping\r
- * array elements only included once.\r
- * <p>The order of elements in the original arrays is preserved\r
- * (with the exception of overlapping elements, which are only\r
- * included on their first occurence).\r
- * @param array1 the first array (can be <code>null</code>)\r
- * @param array2 the second array (can be <code>null</code>)\r
- * @return the new array (<code>null</code> if both given arrays were <code>null</code>)\r
- */\r
- public static String[] mergeStringArrays(String[] array1, String[] array2) {\r
- if (ObjectUtils.isEmpty(array1)) {\r
- return array2;\r
- }\r
- if (ObjectUtils.isEmpty(array2)) {\r
- return array1;\r
- }\r
- List result = new ArrayList();\r
- result.addAll(Arrays.asList(array1));\r
- for (int i = 0; i < array2.length; i++) {\r
- String str = array2[i];\r
- if (!result.contains(str)) {\r
- result.add(str);\r
- }\r
- }\r
- return toStringArray(result);\r
- }\r
-\r
- /**\r
- * Turn given source String array into sorted array.\r
- * @param array the source array\r
- * @return the sorted array (never <code>null</code>)\r
- */\r
- public static String[] sortStringArray(String[] array) {\r
- if (ObjectUtils.isEmpty(array)) {\r
- return new String[0];\r
- }\r
- Arrays.sort(array);\r
- return array;\r
- }\r
-\r
- /**\r
- * Copy the given Collection into a String array.\r
- * The Collection must contain String elements only.\r
- * @param collection the Collection to copy\r
- * @return the String array (<code>null</code> if the passed-in\r
- * Collection was <code>null</code>)\r
- */\r
- public static String[] toStringArray(Collection collection) {\r
- if (collection == null) {\r
- return null;\r
- }\r
- return (String[]) collection.toArray(new String[collection.size()]);\r
- }\r
-\r
- /**\r
- * Copy the given Enumeration into a String array.\r
- * The Enumeration must contain String elements only.\r
- * @param enumeration the Enumeration to copy\r
- * @return the String array (<code>null</code> if the passed-in\r
- * Enumeration was <code>null</code>)\r
- */\r
- public static String[] toStringArray(Enumeration enumeration) {\r
- if (enumeration == null) {\r
- return null;\r
- }\r
- List list = Collections.list(enumeration);\r
- return (String[]) list.toArray(new String[list.size()]);\r
- }\r
-\r
- /**\r
- * Trim the elements of the given String array,\r
- * calling <code>String.trim()</code> on each of them.\r
- * @param array the original String array\r
- * @return the resulting array (of the same size) with trimmed elements\r
- */\r
- public static String[] trimArrayElements(String[] array) {\r
- if (ObjectUtils.isEmpty(array)) {\r
- return new String[0];\r
- }\r
- String[] result = new String[array.length];\r
- for (int i = 0; i < array.length; i++) {\r
- String element = array[i];\r
- result[i] = (element != null ? element.trim() : null);\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * Remove duplicate Strings from the given array.\r
- * Also sorts the array, as it uses a TreeSet.\r
- * @param array the String array\r
- * @return an array without duplicates, in natural sort order\r
- */\r
- public static String[] removeDuplicateStrings(String[] array) {\r
- if (ObjectUtils.isEmpty(array)) {\r
- return array;\r
- }\r
- Set set = new TreeSet();\r
- for (int i = 0; i < array.length; i++) {\r
- set.add(array[i]);\r
- }\r
- return toStringArray(set);\r
- }\r
-\r
- /**\r
- * Split a String at the first occurrence of the delimiter.\r
- * Does not include the delimiter in the result.\r
- * @param toSplit the string to split\r
- * @param delimiter to split the string up with\r
- * @return a two element array with index 0 being before the delimiter, and\r
- * index 1 being after the delimiter (neither element includes the delimiter);\r
- * or <code>null</code> if the delimiter wasn't found in the given input String\r
- */\r
- public static String[] split(String toSplit, String delimiter) {\r
- if (!hasLength(toSplit) || !hasLength(delimiter)) {\r
- return null;\r
- }\r
- int offset = toSplit.indexOf(delimiter);\r
- if (offset < 0) {\r
- return null;\r
- }\r
- String beforeDelimiter = toSplit.substring(0, offset);\r
- String afterDelimiter = toSplit.substring(offset + delimiter.length());\r
- return new String[] {beforeDelimiter, afterDelimiter};\r
- }\r
-\r
- /**\r
- * Take an array Strings and split each element based on the given delimiter.\r
- * A <code>Properties</code> instance is then generated, with the left of the\r
- * delimiter providing the key, and the right of the delimiter providing the value.\r
- * <p>Will trim both the key and value before adding them to the\r
- * <code>Properties</code> instance.\r
- * @param array the array to process\r
- * @param delimiter to split each element using (typically the equals symbol)\r
- * @return a <code>Properties</code> instance representing the array contents,\r
- * or <code>null</code> if the array to process was null or empty\r
- */\r
- public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {\r
- return splitArrayElementsIntoProperties(array, delimiter, null);\r
- }\r
-\r
- /**\r
- * Take an array Strings and split each element based on the given delimiter.\r
- * A <code>Properties</code> instance is then generated, with the left of the\r
- * delimiter providing the key, and the right of the delimiter providing the value.\r
- * <p>Will trim both the key and value before adding them to the\r
- * <code>Properties</code> instance.\r
- * @param array the array to process\r
- * @param delimiter to split each element using (typically the equals symbol)\r
- * @param charsToDelete one or more characters to remove from each element\r
- * prior to attempting the split operation (typically the quotation mark\r
- * symbol), or <code>null</code> if no removal should occur\r
- * @return a <code>Properties</code> instance representing the array contents,\r
- * or <code>null</code> if the array to process was <code>null</code> or empty\r
- */\r
- public static Properties splitArrayElementsIntoProperties(\r
- String[] array, String delimiter, String charsToDelete) {\r
-\r
- if (ObjectUtils.isEmpty(array)) {\r
- return null;\r
- }\r
- Properties result = new Properties();\r
- for (int i = 0; i < array.length; i++) {\r
- String element = array[i];\r
- if (charsToDelete != null) {\r
- element = deleteAny(array[i], charsToDelete);\r
- }\r
- String[] splittedElement = split(element, delimiter);\r
- if (splittedElement == null) {\r
- continue;\r
- }\r
- result.setProperty(splittedElement[0].trim(), splittedElement[1].trim());\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * Tokenize the given String into a String array via a StringTokenizer.\r
- * Trims tokens and omits empty tokens.\r
- * <p>The given delimiters string is supposed to consist of any number of\r
- * delimiter characters. Each of those characters can be used to separate\r
- * tokens. A delimiter is always a single character; for multi-character\r
- * delimiters, consider using <code>delimitedListToStringArray</code>\r
- * @param str the String to tokenize\r
- * @param delimiters the delimiter characters, assembled as String\r
- * (each of those characters is individually considered as delimiter).\r
- * @return an array of the tokens\r
- * @see java.util.StringTokenizer\r
- * @see java.lang.String#trim()\r
- * @see #delimitedListToStringArray\r
- */\r
- public static String[] tokenizeToStringArray(String str, String delimiters) {\r
- return tokenizeToStringArray(str, delimiters, true, true);\r
- }\r
-\r
- /**\r
- * Tokenize the given String into a String array via a StringTokenizer.\r
- * <p>The given delimiters string is supposed to consist of any number of\r
- * delimiter characters. Each of those characters can be used to separate\r
- * tokens. A delimiter is always a single character; for multi-character\r
- * delimiters, consider using <code>delimitedListToStringArray</code>\r
- * @param str the String to tokenize\r
- * @param delimiters the delimiter characters, assembled as String\r
- * (each of those characters is individually considered as delimiter)\r
- * @param trimTokens trim the tokens via String's <code>trim</code>\r
- * @param ignoreEmptyTokens omit empty tokens from the result array\r
- * (only applies to tokens that are empty after trimming; StringTokenizer\r
- * will not consider subsequent delimiters as token in the first place).\r
- * @return an array of the tokens (<code>null</code> if the input String\r
- * was <code>null</code>)\r
- * @see java.util.StringTokenizer\r
- * @see java.lang.String#trim()\r
- * @see #delimitedListToStringArray\r
- */\r
- public static String[] tokenizeToStringArray(\r
- String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {\r
-\r
- if (str == null) {\r
- return null;\r
- }\r
- StringTokenizer st = new StringTokenizer(str, delimiters);\r
- List tokens = new ArrayList();\r
- while (st.hasMoreTokens()) {\r
- String token = st.nextToken();\r
- if (trimTokens) {\r
- token = token.trim();\r
- }\r
- if (!ignoreEmptyTokens || token.length() > 0) {\r
- tokens.add(token);\r
- }\r
- }\r
- return toStringArray(tokens);\r
- }\r
-\r
- /**\r
- * Take a String which is a delimited list and convert it to a String array.\r
- * <p>A single delimiter can consists of more than one character: It will still\r
- * be considered as single delimiter string, rather than as bunch of potential\r
- * delimiter characters - in contrast to <code>tokenizeToStringArray</code>.\r
- * @param str the input String\r
- * @param delimiter the delimiter between elements (this is a single delimiter,\r
- * rather than a bunch individual delimiter characters)\r
- * @return an array of the tokens in the list\r
- * @see #tokenizeToStringArray\r
- */\r
- public static String[] delimitedListToStringArray(String str, String delimiter) {\r
- return delimitedListToStringArray(str, delimiter, null);\r
- }\r
-\r
- /**\r
- * Take a String which is a delimited list and convert it to a String array.\r
- * <p>A single delimiter can consists of more than one character: It will still\r
- * be considered as single delimiter string, rather than as bunch of potential\r
- * delimiter characters - in contrast to <code>tokenizeToStringArray</code>.\r
- * @param str the input String\r
- * @param delimiter the delimiter between elements (this is a single delimiter,\r
- * rather than a bunch individual delimiter characters)\r
- * @param charsToDelete a set of characters to delete. Useful for deleting unwanted\r
- * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String.\r
- * @return an array of the tokens in the list\r
- * @see #tokenizeToStringArray\r
- */\r
- public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {\r
- if (str == null) {\r
- return new String[0];\r
- }\r
- if (delimiter == null) {\r
- return new String[] {str};\r
- }\r
- List result = new ArrayList();\r
- if ("".equals(delimiter)) {\r
- for (int i = 0; i < str.length(); i++) {\r
- result.add(deleteAny(str.substring(i, i + 1), charsToDelete));\r
- }\r
- }\r
- else {\r
- int pos = 0;\r
- int delPos = 0;\r
- while ((delPos = str.indexOf(delimiter, pos)) != -1) {\r
- result.add(deleteAny(str.substring(pos, delPos), charsToDelete));\r
- pos = delPos + delimiter.length();\r
- }\r
- if (str.length() > 0 && pos <= str.length()) {\r
- // Add rest of String, but not in case of empty input.\r
- result.add(deleteAny(str.substring(pos), charsToDelete));\r
- }\r
- }\r
- return toStringArray(result);\r
- }\r
-\r
- /**\r
- * Convert a CSV list into an array of Strings.\r
- * @param str the input String\r
- * @return an array of Strings, or the empty array in case of empty input\r
- */\r
- public static String[] commaDelimitedListToStringArray(String str) {\r
- return delimitedListToStringArray(str, ",");\r
- }\r
-\r
- /**\r
- * Convenience method to convert a CSV string list to a set.\r
- * Note that this will suppress duplicates.\r
- * @param str the input String\r
- * @return a Set of String entries in the list\r
- */\r
- public static Set commaDelimitedListToSet(String str) {\r
- Set set = new TreeSet();\r
- String[] tokens = commaDelimitedListToStringArray(str);\r
- for (int i = 0; i < tokens.length; i++) {\r
- set.add(tokens[i]);\r
- }\r
- return set;\r
- }\r
-\r
- /**\r
- * Convenience method to return a Collection as a delimited (e.g. CSV)\r
- * String. E.g. useful for <code>toString()</code> implementations.\r
- * @param coll the Collection to display\r
- * @param delim the delimiter to use (probably a ",")\r
- * @param prefix the String to start each element with\r
- * @param suffix the String to end each element with\r
- * @return the delimited String\r
- */\r
- public static String collectionToDelimitedString(Collection coll, String delim, String prefix, String suffix) {\r
- if (CollectionUtils.isEmpty(coll)) {\r
- return "";\r
- }\r
- StringBuffer sb = new StringBuffer();\r
- Iterator it = coll.iterator();\r
- while (it.hasNext()) {\r
- sb.append(prefix).append(it.next()).append(suffix);\r
- if (it.hasNext()) {\r
- sb.append(delim);\r
- }\r
- }\r
- return sb.toString();\r
- }\r
-\r
- /**\r
- * Convenience method to return a Collection as a delimited (e.g. CSV)\r
- * String. E.g. useful for <code>toString()</code> implementations.\r
- * @param coll the Collection to display\r
- * @param delim the delimiter to use (probably a ",")\r
- * @return the delimited String\r
- */\r
- public static String collectionToDelimitedString(Collection coll, String delim) {\r
- return collectionToDelimitedString(coll, delim, "", "");\r
- }\r
-\r
- /**\r
- * Convenience method to return a Collection as a CSV String.\r
- * E.g. useful for <code>toString()</code> implementations.\r
- * @param coll the Collection to display\r
- * @return the delimited String\r
- */\r
- public static String collectionToCommaDelimitedString(Collection coll) {\r
- return collectionToDelimitedString(coll, ",");\r
- }\r
-\r
- /**\r
- * Convenience method to return a String array as a delimited (e.g. CSV)\r
- * String. E.g. useful for <code>toString()</code> implementations.\r
- * @param arr the array to display\r
- * @param delim the delimiter to use (probably a ",")\r
- * @return the delimited String\r
- */\r
- public static String arrayToDelimitedString(Object[] arr, String delim) {\r
- if (ObjectUtils.isEmpty(arr)) {\r
- return "";\r
- }\r
- StringBuffer sb = new StringBuffer();\r
- for (int i = 0; i < arr.length; i++) {\r
- if (i > 0) {\r
- sb.append(delim);\r
- }\r
- sb.append(arr[i]);\r
- }\r
- return sb.toString();\r
- }\r
-\r
- /**\r
- * Convenience method to return a String array as a CSV String.\r
- * E.g. useful for <code>toString()</code> implementations.\r
- * @param arr the array to display\r
- * @return the delimited String\r
- */\r
- public static String arrayToCommaDelimitedString(Object[] arr) {\r
- return arrayToDelimitedString(arr, ",");\r
- }\r
-\r
-}\r
+++ /dev/null
-/*\r
- * Copyright 2002-2008 the original author or authors.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package org.argeo.osgi.boot.internal.springutil;\r
-\r
-/**\r
- * Helper class for resolving placeholders in texts. Usually applied to file paths.\r
- *\r
- * <p>A text may contain <code>${...}</code> placeholders, to be resolved as\r
- * system properties: e.g. <code>${user.dir}</code>.\r
- *\r
- * @author Juergen Hoeller\r
- * @since 1.2.5\r
- * @see #PLACEHOLDER_PREFIX\r
- * @see #PLACEHOLDER_SUFFIX\r
- * @see System#getProperty(String)\r
- */\r
-public abstract class SystemPropertyUtils {\r
-\r
- /** Prefix for system property placeholders: "${" */\r
- public static final String PLACEHOLDER_PREFIX = "${";\r
-\r
- /** Suffix for system property placeholders: "}" */\r
- public static final String PLACEHOLDER_SUFFIX = "}";\r
-\r
-\r
- /**\r
- * Resolve ${...} placeholders in the given text,\r
- * replacing them with corresponding system property values.\r
- * @param text the String to resolve\r
- * @return the resolved String\r
- * @see #PLACEHOLDER_PREFIX\r
- * @see #PLACEHOLDER_SUFFIX\r
- */\r
- @SuppressWarnings("unused")\r
- public static String resolvePlaceholders(String text) {\r
- StringBuffer buf = new StringBuffer(text);\r
-\r
- int startIndex = buf.indexOf(PLACEHOLDER_PREFIX);\r
- while (startIndex != -1) {\r
- int endIndex = buf.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length());\r
- if (endIndex != -1) {\r
- String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);\r
- int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length();\r
- try {\r
- String propVal = System.getProperty(placeholder);\r
- if (propVal == null) {\r
- // Fall back to searching the system environment.\r
- //propVal = System.getenv(placeholder);// mbaudier - 2009-07-26\r
- throw new Error("getenv no longer supported, use properties and -D instead: " + placeholder);\r
- }\r
- if (propVal != null) {\r
- buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal);\r
- nextIndex = startIndex + propVal.length();\r
- }\r
- else {\r
- System.err.println("Could not resolve placeholder '" + placeholder + "' in [" + text +\r
- "] as system property: neither system property nor environment variable found");\r
- }\r
- }\r
- catch (Throwable ex) {\r
- System.err.println("Could not resolve placeholder '" + placeholder + "' in [" + text +\r
- "] as system property: " + ex);\r
- }\r
- startIndex = buf.indexOf(PLACEHOLDER_PREFIX, nextIndex);\r
- }\r
- else {\r
- startIndex = -1;\r
- }\r
- }\r
-\r
- return buf.toString();\r
- }\r
-\r
-}\r
+++ /dev/null
-log4j.rootLogger=WARN, console
-
-log4j.logger.org.argeo=INFO
-
-## Appenders
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c - [%t]%n
-
-log4j.appender.development=org.apache.log4j.ConsoleAppender
-log4j.appender.development.layout=org.apache.log4j.PatternLayout
-log4j.appender.development.layout.ConversionPattern=%d{ABSOLUTE} %m (%F:%L) [%t] %p %n
+++ /dev/null
-grant {
- permission java.security.AllPermission;
-};
\ No newline at end of file
+++ /dev/null
-/** Simple OSGi initialisation. */
-package org.argeo.osgi.boot;
\ No newline at end of file
</properties>
<modules>
<!-- Base -->
- <module>org.argeo.osgi.boot</module>
+ <module>org.argeo.init</module>
<module>org.argeo.enterprise</module>
<!-- <module>org.argeo.jcr</module> -->
<!-- <module>org.argeo.core</module> -->