Rename OSGi Boot to Argeo Init and introduce logging framework.
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 29 Dec 2021 10:29:45 +0000 (11:29 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 29 Dec 2021 10:29:45 +0000 (11:29 +0100)
90 files changed:
demo/argeo_node_osgiboot.properties
org.argeo.init/.classpath [new file with mode: 0644]
org.argeo.init/.project [new file with mode: 0644]
org.argeo.init/META-INF/.gitignore [new file with mode: 0644]
org.argeo.init/bnd.bnd [new file with mode: 0644]
org.argeo.init/build.properties [new file with mode: 0644]
org.argeo.init/pom.xml [new file with mode: 0644]
org.argeo.init/src/META-INF/services/java.lang.System$LoggerFinder [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/RuntimeContext.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/Service.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/StaticRuntimeContext.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/A2Branch.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/A2Component.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/A2Contribution.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/A2Exception.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/A2Module.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/A2Source.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/FsA2Source.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/FsM2Source.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/OsgiContext.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/ProvisioningSource.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/a2/package-info.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/logging/ThinLogEntry.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/logging/ThinLoggerFinder.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/logging/ThinLogging.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/Activator.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/AdminThread.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/BundlesSet.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/DistributionBundle.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/Launcher.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/Main.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/NodeRunner.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/OsgiBootConstants.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/OsgiBootDiagnostics.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/OsgiBootUtils.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/OsgiBuilder.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/log4j.properties [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/node.policy [new file with mode: 0644]
org.argeo.init/src/org/argeo/init/osgi/package-info.java [new file with mode: 0644]
org.argeo.osgi.boot/.classpath [deleted file]
org.argeo.osgi.boot/.project [deleted file]
org.argeo.osgi.boot/META-INF/.gitignore [deleted file]
org.argeo.osgi.boot/bnd.bnd [deleted file]
org.argeo.osgi.boot/build.properties [deleted file]
org.argeo.osgi.boot/pom.xml [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Branch.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Component.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Contribution.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Exception.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Module.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Source.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/AbstractProvisioningSource.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/ClasspathSource.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/FsA2Source.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/FsM2Source.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/OsgiContext.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/ProvisioningManager.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/ProvisioningSource.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/a2/package-info.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/Activator.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/AdminThread.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/BundlesSet.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/DistributionBundle.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/Launcher.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/Main.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/NodeRunner.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootConstants.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootDiagnostics.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootException.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootUtils.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBuilder.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/equinox/EquinoxUtils.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/equinox/package-info.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/AntPathMatcher.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/Bootstrap.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/CollectionUtils.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/ObjectUtils.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/PathMatcher.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/StringUtils.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/SystemPropertyUtils.java [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/log4j.properties [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/node.policy [deleted file]
org.argeo.osgi.boot/src/org/argeo/osgi/boot/package-info.java [deleted file]
pom.xml

index 64ff5d54c1c828c63920edb628b2442011ff0c1a..2a8c3513b65d8af1537824e50f9b771435fe4625 100644 (file)
@@ -1,16 +1,16 @@
-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
diff --git a/org.argeo.init/.classpath b/org.argeo.init/.classpath
new file mode 100644 (file)
index 0000000..71eb167
--- /dev/null
@@ -0,0 +1,11 @@
+<?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>
diff --git a/org.argeo.init/.project b/org.argeo.init/.project
new file mode 100644 (file)
index 0000000..13443df
--- /dev/null
@@ -0,0 +1,23 @@
+<?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>
diff --git a/org.argeo.init/META-INF/.gitignore b/org.argeo.init/META-INF/.gitignore
new file mode 100644 (file)
index 0000000..4854a41
--- /dev/null
@@ -0,0 +1 @@
+/MANIFEST.MF
diff --git a/org.argeo.init/bnd.bnd b/org.argeo.init/bnd.bnd
new file mode 100644 (file)
index 0000000..f421512
--- /dev/null
@@ -0,0 +1,8 @@
+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: !*
diff --git a/org.argeo.init/build.properties b/org.argeo.init/build.properties
new file mode 100644 (file)
index 0000000..d88c54b
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/,\
+           ext/test/
+additional.bundles = org.junit,\
+                     org.hamcrest
diff --git a/org.argeo.init/pom.xml b/org.argeo.init/pom.xml
new file mode 100644 (file)
index 0000000..85b43d9
--- /dev/null
@@ -0,0 +1,46 @@
+<?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
diff --git a/org.argeo.init/src/META-INF/services/java.lang.System$LoggerFinder b/org.argeo.init/src/META-INF/services/java.lang.System$LoggerFinder
new file mode 100644 (file)
index 0000000..f63dfce
--- /dev/null
@@ -0,0 +1 @@
+org.argeo.init.logging.ThinLoggerFinder
\ No newline at end of file
diff --git a/org.argeo.init/src/org/argeo/init/RuntimeContext.java b/org.argeo.init/src/org/argeo/init/RuntimeContext.java
new file mode 100644 (file)
index 0000000..7dd8e6c
--- /dev/null
@@ -0,0 +1,5 @@
+package org.argeo.init;
+
+public interface RuntimeContext extends Runnable, AutoCloseable {
+       void waitForStop(long timeout) throws InterruptedException;
+}
diff --git a/org.argeo.init/src/org/argeo/init/Service.java b/org.argeo.init/src/org/argeo/init/Service.java
new file mode 100644 (file)
index 0000000..9a01cff
--- /dev/null
@@ -0,0 +1,59 @@
+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);
+               }
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/StaticRuntimeContext.java b/org.argeo.init/src/org/argeo/init/StaticRuntimeContext.java
new file mode 100644 (file)
index 0000000..e01e619
--- /dev/null
@@ -0,0 +1,35 @@
+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;
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/A2Branch.java b/org.argeo.init/src/org/argeo/init/a2/A2Branch.java
new file mode 100644 (file)
index 0000000..9713d01
--- /dev/null
@@ -0,0 +1,85 @@
+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();
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/A2Component.java b/org.argeo.init/src/org/argeo/init/a2/A2Component.java
new file mode 100644 (file)
index 0000000..2b6814f
--- /dev/null
@@ -0,0 +1,100 @@
+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(')');
+               }
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/A2Contribution.java b/org.argeo.init/src/org/argeo/init/a2/A2Contribution.java
new file mode 100644 (file)
index 0000000..3d33b55
--- /dev/null
@@ -0,0 +1,84 @@
+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');
+               }
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/A2Exception.java b/org.argeo.init/src/org/argeo/init/a2/A2Exception.java
new file mode 100644 (file)
index 0000000..6ba87a7
--- /dev/null
@@ -0,0 +1,15 @@
+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);
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/A2Module.java b/org.argeo.init/src/org/argeo/init/a2/A2Module.java
new file mode 100644 (file)
index 0000000..b862c54
--- /dev/null
@@ -0,0 +1,62 @@
+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;
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/A2Source.java b/org.argeo.init/src/org/argeo/init/a2/A2Source.java
new file mode 100644 (file)
index 0000000..388a850
--- /dev/null
@@ -0,0 +1,7 @@
+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 + ":///";
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java b/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java
new file mode 100644 (file)
index 0000000..f43a616
--- /dev/null
@@ -0,0 +1,212 @@
+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());
+               }
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java b/org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java
new file mode 100644 (file)
index 0000000..8a9e5e6
--- /dev/null
@@ -0,0 +1,38 @@
+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);
+               }
+
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java b/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java
new file mode 100644 (file)
index 0000000..949dbdf
--- /dev/null
@@ -0,0 +1,88 @@
+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();
+               }
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/FsM2Source.java b/org.argeo.init/src/org/argeo/init/a2/FsM2Source.java
new file mode 100644 (file)
index 0000000..1657fad
--- /dev/null
@@ -0,0 +1,66 @@
+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();
+               }
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/OsgiContext.java b/org.argeo.init/src/org/argeo/init/a2/OsgiContext.java
new file mode 100644 (file)
index 0000000..35fbee3
--- /dev/null
@@ -0,0 +1,39 @@
+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() + ")");
+               }
+
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java b/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java
new file mode 100644 (file)
index 0000000..6012c34
--- /dev/null
@@ -0,0 +1,200 @@
+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();
+                       }
+               }
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/ProvisioningSource.java b/org.argeo.init/src/org/argeo/init/a2/ProvisioningSource.java
new file mode 100644 (file)
index 0000000..9935630
--- /dev/null
@@ -0,0 +1,21 @@
+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);
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/a2/package-info.java b/org.argeo.init/src/org/argeo/init/a2/package-info.java
new file mode 100644 (file)
index 0000000..bb8fa6e
--- /dev/null
@@ -0,0 +1,2 @@
+/** A2 OSGi repository format. */
+package org.argeo.init.a2;
\ No newline at end of file
diff --git a/org.argeo.init/src/org/argeo/init/logging/ThinLogEntry.java b/org.argeo.init/src/org/argeo/init/logging/ThinLogEntry.java
new file mode 100644 (file)
index 0000000..c71fb6d
--- /dev/null
@@ -0,0 +1,90 @@
+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;
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/logging/ThinLoggerFinder.java b/org.argeo.init/src/org/argeo/init/logging/ThinLoggerFinder.java
new file mode 100644 (file)
index 0000000..7849309
--- /dev/null
@@ -0,0 +1,19 @@
+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);
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java b/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java
new file mode 100644 (file)
index 0000000..e7edc19
--- /dev/null
@@ -0,0 +1,284 @@
+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());
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/Activator.java b/org.argeo.init/src/org/argeo/init/osgi/Activator.java
new file mode 100644 (file)
index 0000000..5a95270
--- /dev/null
@@ -0,0 +1,56 @@
+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();
+               }
+
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/AdminThread.java b/org.argeo.init/src/org/argeo/init/osgi/AdminThread.java
new file mode 100644 (file)
index 0000000..fdb6fe9
--- /dev/null
@@ -0,0 +1,53 @@
+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);
+                       }
+               }
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/BundlesSet.java b/org.argeo.init/src/org/argeo/init/osgi/BundlesSet.java
new file mode 100644 (file)
index 0000000..73ab9af
--- /dev/null
@@ -0,0 +1,71 @@
+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;
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/DistributionBundle.java b/org.argeo.init/src/org/argeo/init/osgi/DistributionBundle.java
new file mode 100644 (file)
index 0000000..35b66e6
--- /dev/null
@@ -0,0 +1,269 @@
+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;
+               }
+
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/Launcher.java b/org.argeo.init/src/org/argeo/init/osgi/Launcher.java
new file mode 100644 (file)
index 0000000..778c08a
--- /dev/null
@@ -0,0 +1,132 @@
+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);
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/Main.java b/org.argeo.init/src/org/argeo/init/osgi/Main.java
new file mode 100644 (file)
index 0000000..ce83329
--- /dev/null
@@ -0,0 +1,35 @@
+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();
+
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/NodeRunner.java b/org.argeo.init/src/org/argeo/init/osgi/NodeRunner.java
new file mode 100644 (file)
index 0000000..3369650
--- /dev/null
@@ -0,0 +1,235 @@
+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>");
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java
new file mode 100644 (file)
index 0000000..aeec0b7
--- /dev/null
@@ -0,0 +1,752 @@
+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
+        */
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiBootConstants.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiBootConstants.java
new file mode 100644 (file)
index 0000000..e45f826
--- /dev/null
@@ -0,0 +1,5 @@
+package org.argeo.init.osgi;
+
+public interface OsgiBootConstants {
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiBootDiagnostics.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiBootDiagnostics.java
new file mode 100644 (file)
index 0000000..72d9a3e
--- /dev/null
@@ -0,0 +1,78 @@
+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;
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiBootUtils.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiBootUtils.java
new file mode 100644 (file)
index 0000000..d8efe83
--- /dev/null
@@ -0,0 +1,145 @@
+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, &lt;0: tested version is newer, &gt;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<>();
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiBuilder.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiBuilder.java
new file mode 100644 (file)
index 0000000..39c42cb
--- /dev/null
@@ -0,0 +1,319 @@
+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;
+               }
+       }
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java
new file mode 100644 (file)
index 0000000..91756e3
--- /dev/null
@@ -0,0 +1,52 @@
+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();
+       }
+
+}
diff --git a/org.argeo.init/src/org/argeo/init/osgi/log4j.properties b/org.argeo.init/src/org/argeo/init/osgi/log4j.properties
new file mode 100644 (file)
index 0000000..1fcf25e
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/org.argeo.init/src/org/argeo/init/osgi/node.policy b/org.argeo.init/src/org/argeo/init/osgi/node.policy
new file mode 100644 (file)
index 0000000..facb613
--- /dev/null
@@ -0,0 +1,3 @@
+grant {
+  permission java.security.AllPermission;
+};
\ No newline at end of file
diff --git a/org.argeo.init/src/org/argeo/init/osgi/package-info.java b/org.argeo.init/src/org/argeo/init/osgi/package-info.java
new file mode 100644 (file)
index 0000000..993e246
--- /dev/null
@@ -0,0 +1,2 @@
+/** Simple OSGi initialisation. */
+package org.argeo.init.osgi;
\ No newline at end of file
diff --git a/org.argeo.osgi.boot/.classpath b/org.argeo.osgi.boot/.classpath
deleted file mode 100644 (file)
index 71eb167..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?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>
diff --git a/org.argeo.osgi.boot/.project b/org.argeo.osgi.boot/.project
deleted file mode 100644 (file)
index e145e96..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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>
diff --git a/org.argeo.osgi.boot/META-INF/.gitignore b/org.argeo.osgi.boot/META-INF/.gitignore
deleted file mode 100644 (file)
index 4854a41..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/MANIFEST.MF
diff --git a/org.argeo.osgi.boot/bnd.bnd b/org.argeo.osgi.boot/bnd.bnd
deleted file mode 100644 (file)
index fd2b18c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-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,\
-*
diff --git a/org.argeo.osgi.boot/build.properties b/org.argeo.osgi.boot/build.properties
deleted file mode 100644 (file)
index d88c54b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-source.. = src/,\
-           ext/test/
-additional.bundles = org.junit,\
-                     org.hamcrest
diff --git a/org.argeo.osgi.boot/pom.xml b/org.argeo.osgi.boot/pom.xml
deleted file mode 100644 (file)
index 554471b..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-<?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
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Branch.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Branch.java
deleted file mode 100644 (file)
index 070830e..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-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();
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Component.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Component.java
deleted file mode 100644 (file)
index 0b5d13c..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-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(')');
-               }
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Contribution.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Contribution.java
deleted file mode 100644 (file)
index 35d5f9b..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-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');
-               }
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Exception.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Exception.java
deleted file mode 100644 (file)
index 83c743a..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-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);
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Module.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Module.java
deleted file mode 100644 (file)
index 77f81d1..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-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;
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Source.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Source.java
deleted file mode 100644 (file)
index 2c88a38..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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 + ":///";
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/AbstractProvisioningSource.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/AbstractProvisioningSource.java
deleted file mode 100644 (file)
index 32c175d..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-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());
-               }
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ClasspathSource.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ClasspathSource.java
deleted file mode 100644 (file)
index ea34666..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-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);
-               }
-
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/FsA2Source.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/FsA2Source.java
deleted file mode 100644 (file)
index eea59de..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-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();
-               }
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/FsM2Source.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/FsM2Source.java
deleted file mode 100644 (file)
index a4c3ed5..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-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();
-               }
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/OsgiContext.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/OsgiContext.java
deleted file mode 100644 (file)
index 8630dc2..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-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() + ")");
-               }
-
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ProvisioningManager.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ProvisioningManager.java
deleted file mode 100644 (file)
index d8246f1..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-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();
-                       }
-               }
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ProvisioningSource.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ProvisioningSource.java
deleted file mode 100644 (file)
index 7d6fadf..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-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);
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/package-info.java b/org.argeo.osgi.boot/src/org/argeo/osgi/a2/package-info.java
deleted file mode 100644 (file)
index b381410..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** A2 OSGi repository format. */
-package org.argeo.osgi.a2;
\ No newline at end of file
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Activator.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Activator.java
deleted file mode 100644 (file)
index 516ab90..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-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 {
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/AdminThread.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/AdminThread.java
deleted file mode 100644 (file)
index b0144a9..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-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);
-                       }
-               }
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/BundlesSet.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/BundlesSet.java
deleted file mode 100644 (file)
index 4a342fd..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-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;
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/DistributionBundle.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/DistributionBundle.java
deleted file mode 100644 (file)
index a336b64..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-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;
-               }
-
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Launcher.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Launcher.java
deleted file mode 100644 (file)
index f67f45b..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-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);
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Main.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Main.java
deleted file mode 100644 (file)
index 45094a7..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-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();
-
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/NodeRunner.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/NodeRunner.java
deleted file mode 100644 (file)
index 263a4cd..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-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>");
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java
deleted file mode 100644 (file)
index b49e720..0000000
+++ /dev/null
@@ -1,753 +0,0 @@
-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
-        */
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootConstants.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootConstants.java
deleted file mode 100644 (file)
index e2d7719..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.argeo.osgi.boot;
-
-public interface OsgiBootConstants {
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootDiagnostics.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootDiagnostics.java
deleted file mode 100644 (file)
index 24a3317..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-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;
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootException.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootException.java
deleted file mode 100644 (file)
index fd6bf65..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-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);
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootUtils.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootUtils.java
deleted file mode 100644 (file)
index c152ed7..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-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, &lt;0: tested version is newer, &gt;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;
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBuilder.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBuilder.java
deleted file mode 100644 (file)
index 8c460e1..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-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;
-               }
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/equinox/EquinoxUtils.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/equinox/EquinoxUtils.java
deleted file mode 100644 (file)
index 55cd067..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-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() {
-
-       }
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/equinox/package-info.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/equinox/package-info.java
deleted file mode 100644 (file)
index b503752..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Simple Eclipse Equinox initialisation. */
-package org.argeo.osgi.boot.equinox;
\ No newline at end of file
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/AntPathMatcher.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/AntPathMatcher.java
deleted file mode 100644 (file)
index e3fc6c2..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-/*\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/&#42;&#42;/test.jsp</code> - matches all <code>test.jsp</code>\r
- * files underneath the <code>com</code> path</li>\r
- * <li><code>org/springframework/&#42;&#42;/*.jsp</code> - matches all <code>.jsp</code>\r
- * files underneath the <code>org/springframework</code> path</li>\r
- * <li><code>org/&#42;&#42;/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
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/Bootstrap.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/Bootstrap.java
deleted file mode 100644 (file)
index 2ac4562..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-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();
-               }
-
-       }
-
-}
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/CollectionUtils.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/CollectionUtils.java
deleted file mode 100644 (file)
index 18cbe16..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-/*\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
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/ObjectUtils.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/ObjectUtils.java
deleted file mode 100644 (file)
index 2c98b46..0000000
+++ /dev/null
@@ -1,833 +0,0 @@
-/*\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
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/PathMatcher.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/PathMatcher.java
deleted file mode 100644 (file)
index d7a2322..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*\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
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/StringUtils.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/StringUtils.java
deleted file mode 100644 (file)
index 6cbaee8..0000000
+++ /dev/null
@@ -1,1113 +0,0 @@
-/*\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
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/SystemPropertyUtils.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/SystemPropertyUtils.java
deleted file mode 100644 (file)
index ff81a22..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*\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
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/log4j.properties b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/log4j.properties
deleted file mode 100644 (file)
index 1fcf25e..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/node.policy b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/node.policy
deleted file mode 100644 (file)
index facb613..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-grant {
-  permission java.security.AllPermission;
-};
\ No newline at end of file
diff --git a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/package-info.java b/org.argeo.osgi.boot/src/org/argeo/osgi/boot/package-info.java
deleted file mode 100644 (file)
index 7474d13..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-/** Simple OSGi initialisation. */
-package org.argeo.osgi.boot;
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2721905f89f25ca040b538509cdabc5e4fd66f5e..d199b7ec18b7a50ac0219cd6ef1df4ea3dc9fd8e 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
        </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> -->