From: Mathieu Baudier Date: Wed, 29 Dec 2021 10:29:45 +0000 (+0100) Subject: Rename OSGi Boot to Argeo Init and introduce logging framework. X-Git-Tag: argeo-commons-2.3.5~117 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=b7d8618ce593bbeca7e311d32a4d98988e27f877;p=lgpl%2Fargeo-commons.git Rename OSGi Boot to Argeo Init and introduce logging framework. --- diff --git a/demo/argeo_node_osgiboot.properties b/demo/argeo_node_osgiboot.properties index 64ff5d54c..2a8c3513b 100644 --- a/demo/argeo_node_osgiboot.properties +++ b/demo/argeo_node_osgiboot.properties @@ -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 index 000000000..71eb16789 --- /dev/null +++ b/org.argeo.init/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/org.argeo.init/.project b/org.argeo.init/.project new file mode 100644 index 000000000..13443df6c --- /dev/null +++ b/org.argeo.init/.project @@ -0,0 +1,23 @@ + + + org.argeo.init + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + + diff --git a/org.argeo.init/META-INF/.gitignore b/org.argeo.init/META-INF/.gitignore new file mode 100644 index 000000000..4854a41b9 --- /dev/null +++ b/org.argeo.init/META-INF/.gitignore @@ -0,0 +1 @@ +/MANIFEST.MF diff --git a/org.argeo.init/bnd.bnd b/org.argeo.init/bnd.bnd new file mode 100644 index 000000000..f42151273 --- /dev/null +++ b/org.argeo.init/bnd.bnd @@ -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 index 000000000..d88c54b85 --- /dev/null +++ b/org.argeo.init/build.properties @@ -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 index 000000000..85b43d9fe --- /dev/null +++ b/org.argeo.init/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + org.argeo.commons + 2.3-SNAPSHOT + argeo-commons + .. + + org.argeo.init + jar + Init + + + + maven-surefire-plugin + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 index 000000000..f63dfce29 --- /dev/null +++ b/org.argeo.init/src/META-INF/services/java.lang.System$LoggerFinder @@ -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 index 000000000..7dd8e6c0c --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/RuntimeContext.java @@ -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 index 000000000..9a01cffed --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/Service.java @@ -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 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 index 000000000..e01e6194d --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/StaticRuntimeContext.java @@ -0,0 +1,35 @@ +package org.argeo.init; + +import java.util.Map; + +public class StaticRuntimeContext implements RuntimeContext { + private Map config; + + private boolean running = false; + + protected StaticRuntimeContext(Map 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 index 000000000..9713d01d3 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/A2Branch.java @@ -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 { + private final A2Component component; + private final String id; + + final SortedMap 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 index 000000000..2b6814f6b --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/A2Component.java @@ -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 + * Bundle-SymbolicName. This is the equivalent of Maven's artifact + * id. + */ +public class A2Component implements Comparable { + private final A2Contribution contribution; + private final String id; + + final SortedMap 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 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 index 000000000..3d33b55e2 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/A2Contribution.java @@ -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 { + 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 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 index 000000000..6ba87a7d0 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/A2Exception.java @@ -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 index 000000000..b862c5435 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/A2Module.java @@ -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 + * Bundle-SymbolicName and Bundle-version. This is the + * equivalent of the full coordinates of a Maven artifact version. + */ +class A2Module implements Comparable { + 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 index 000000000..388a85012 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/A2Source.java @@ -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 index 000000000..f43a6162a --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/AbstractProvisioningSource.java @@ -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 contributions = Collections.synchronizedSortedMap(new TreeMap<>()); + + public Iterable 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 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() { + 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 index 000000000..8a9e5e67f --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/ClasspathSource.java @@ -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 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 index 000000000..949dbdf81 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/FsA2Source.java @@ -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 contributionPaths = Files.newDirectoryStream(base); + SortedSet 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 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 index 000000000..1657fad57 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/FsM2Source.java @@ -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 { + + @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 index 000000000..35fbee356 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/OsgiContext.java @@ -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 index 000000000..6012c34ee --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/ProvisioningManager.java @@ -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 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 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 update() { + boolean fragmentsUpdated = false; + Set 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 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 index 000000000..99356300e --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/ProvisioningSource.java @@ -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 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 index 000000000..bb8fa6e00 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/a2/package-info.java @@ -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 index 000000000..c71fb6d27 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/logging/ThinLogEntry.java @@ -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; + private final Optional 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 getThrowable() { + return throwable; + } + + public Optional 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 index 000000000..784930946 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/logging/ThinLoggerFinder.java @@ -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 index 000000000..e7edc19a8 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java @@ -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 loggers = new TreeMap<>(); + private NavigableMap 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 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 { + + 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 { + 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 index 000000000..5a9527041 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/Activator.java @@ -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 + * http: + * //wiki.eclipse.org/Configurator + */ +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 getKeys() { + Vector 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 index 000000000..fdb6fe923 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/AdminThread.java @@ -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 index 000000000..73ab9afa7 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/BundlesSet.java @@ -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 includes = new ArrayList(); + private List excludes = new ArrayList(); + + 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 getIncludes() { + return includes; + } + + public List 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 index 000000000..35b66e6b7 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/DistributionBundle.java @@ -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 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 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 res) throws IOException { + try (DirectoryStream 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 listArtifacts(InputStream in) { + List osgiArtifacts = new ArrayList(); + 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 listUrls() { + if (baseUrl == null) + throw new IllegalArgumentException("Base URL is not set"); + + if (artifacts == null) + throw new IllegalStateException("Artifact list not initialized"); + + List urls = new ArrayList(); + 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 index 000000000..778c08a70 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/Launcher.java @@ -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 args = new ArrayList(); + 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 index 000000000..ce83329b6 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/Main.java @@ -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 index 000000000..336965060 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/NodeRunner.java @@ -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 ff = ServiceLoader.load(FrameworkFactory.class); + FrameworkFactory frameworkFactory = ff.iterator().next(); + Map configuration = new HashMap(); + 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 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: "); + } +} 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 index 000000000..aeec0b768 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiBoot.java @@ -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> duplicatePackages = diagnostics.findPackagesExportedTwice(); + if (duplicatePackages.size() > 0) { + OsgiBootUtils.info("Packages exported twice:"); + Iterator it = duplicatePackages.keySet().iterator(); + while (it.hasNext()) { + String pkgName = it.next(); + OsgiBootUtils.info(pkgName); + Set bdles = duplicatePackages.get(pkgName); + Iterator 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 urls = new ArrayList(); + urls.add(url); + installUrls(urls); + return (Bundle) getBundlesByLocation().get(url); + } + + /** Install the bundles at this URL list. */ + public void installUrls(List urls) { + Map 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 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> startLevels = new TreeMap>(); + computeStartLevels(startLevels, properties, defaultStartLevel); + // inverts the map for the time being, TODO optimise + Map 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> 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 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> startLevels, Integer startLevel, + String str) { + if (str == null || str.trim().equals("")) + return; + + if (!startLevels.containsKey(startLevel)) + startLevels.put(startLevel, new ArrayList()); + 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 bundlesToStart) { + if (bundlesToStart.size() == 0) + return true; + + // used to monitor ACTIVE states + List startedBundles = new ArrayList(); + // used to log the bundles not found + List notFoundBundles = new ArrayList(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 notStarted = new ArrayList(); + while (!allStarted && (System.currentTimeMillis() - beginMonitor) < defaultTimeout) { + notStarted = new ArrayList(); + 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:
+ * /base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar
+ * WARNING: /base/directory;in=*.jar,\ at the end of a file, + * without a new line causes a '.' to be appended with unexpected side effects. + */ + public List 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 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 getBundlesUrls(String baseUrl, String bundlePatterns) { + List urls = new ArrayList(); + if (bundlePatterns == null) + return urls; + +// bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns); + if (OsgiBootUtils.debug) + debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns); + + StringTokenizer st = new StringTokenizer(bundlePatterns, ","); + List bundlesSets = new ArrayList(); + 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 included = new ArrayList(); +// 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 excluded = new ArrayList(); + 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 getDistributionUrls() { + String distributionUrl = getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL); + String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL); + return getDistributionUrls(distributionUrl, baseUrl); + } + + public List getDistributionUrls(String distributionUrl, String baseUrl) { + List urls = new ArrayList(); + 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 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 getBundlesByLocation() { + Map installedBundles = new HashMap(); + 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 getBundlesBySymbolicName() { + Map namedBundles = new HashMap(); + 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 index 000000000..e45f82689 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiBootConstants.java @@ -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 index 000000000..72d9a3e64 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiBootDiagnostics.java @@ -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 packageAdminRef = bundleContext.getServiceReference(PackageAdmin.class); + PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(packageAdminRef); + packageAdmin.resolveBundles(null); + + Bundle[] bundles = bundleContext.getBundles(); + List unresolvedBundles = new ArrayList(); + 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> findPackagesExportedTwice() { + ServiceReference paSr = bundleContext.getServiceReference(PackageAdmin.class); + PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(paSr); + + // find packages exported twice + Bundle[] bundles = bundleContext.getBundles(); + Map> exportedPackages = new TreeMap>(); + 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()); + } + (exportedPackages.get(pkgName)).add(bundle.getSymbolicName() + "_" + bundle.getVersion()); + } + } + Map> duplicatePackages = new TreeMap>(); + Iterator it = exportedPackages.keySet().iterator(); + while (it.hasNext()) { + String pkgName = it.next().toString(); + Set 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 index 000000000..d8efe8343 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiBootUtils.java @@ -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, <0: tested version is newer, >0: + * currentVersion is newer. + */ + public static int compareVersions(String currentVersion, String testedVersion) { + List cToks = new ArrayList(); + StringTokenizer cSt = new StringTokenizer(currentVersion, "."); + while (cSt.hasMoreTokens()) + cToks.add(cSt.nextToken()); + List tToks = new ArrayList(); + 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 configuration) { + Optional 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 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 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 index 000000000..39c42cbf3 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiBuilder.java @@ -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 startLevels = new TreeMap<>(); + private List distributionBundles = new ArrayList<>(); + + private Map configuration = new HashMap(); + 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 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 lst = new ArrayList<>(); + Collections.addAll(lst, bundles.split(",")); + BundleTracker bt = new BundleTracker(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 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 bundles = new HashSet<>(); + + public void add(String bundle) { + String[] b = bundle.split(","); + Collections.addAll(bundles, b); + } + + public Set 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 index 000000000..91756e32b --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java @@ -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 config; + private Framework framework; + private OsgiBoot osgiBoot; + + public OsgiRuntimeContext(Map config) { + this.config = config; + } + + @Override + public void run() { + ServiceLoader sl = ServiceLoader.load(FrameworkFactory.class); + Optional 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 index 000000000..1fcf25e3b --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/log4j.properties @@ -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 index 000000000..facb61327 --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/node.policy @@ -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 index 000000000..993e246cd --- /dev/null +++ b/org.argeo.init/src/org/argeo/init/osgi/package-info.java @@ -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 index 71eb16789..000000000 --- a/org.argeo.osgi.boot/.classpath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/org.argeo.osgi.boot/.project b/org.argeo.osgi.boot/.project deleted file mode 100644 index e145e9691..000000000 --- a/org.argeo.osgi.boot/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - org.argeo.osgi.boot - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.pde.PluginNature - - diff --git a/org.argeo.osgi.boot/META-INF/.gitignore b/org.argeo.osgi.boot/META-INF/.gitignore deleted file mode 100644 index 4854a41b9..000000000 --- a/org.argeo.osgi.boot/META-INF/.gitignore +++ /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 index fd2b18cd1..000000000 --- a/org.argeo.osgi.boot/bnd.bnd +++ /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 index d88c54b85..000000000 --- a/org.argeo.osgi.boot/build.properties +++ /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 index 554471bcc..000000000 --- a/org.argeo.osgi.boot/pom.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - 4.0.0 - - org.argeo.commons - 2.3-SNAPSHOT - argeo-commons - .. - - org.argeo.osgi.boot - jar - OSGi Boot - - - - maven-surefire-plugin - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 index 070830e57..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Branch.java +++ /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 { - private final A2Component component; - private final String id; - - final SortedMap 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 index 0b5d13cce..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Component.java +++ /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 - * Bundle-SymbolicName. This is the equivalent of Maven's artifact - * id. - */ -public class A2Component implements Comparable { - private final A2Contribution contribution; - private final String id; - - final SortedMap 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 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 index 35d5f9b03..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Contribution.java +++ /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 { - 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 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 index 83c743afe..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Exception.java +++ /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 index 77f81d130..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Module.java +++ /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 - * Bundle-SymbolicName and Bundle-version. This is the - * equivalent of the full coordinates of a Maven artifact version. - */ -class A2Module implements Comparable { - 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 index 2c88a385f..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/A2Source.java +++ /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 index 32c175d3a..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/AbstractProvisioningSource.java +++ /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 contributions = Collections.synchronizedSortedMap(new TreeMap<>()); - - public Iterable 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 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() { - 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 index ea346663d..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ClasspathSource.java +++ /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 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 index eea59ded6..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/FsA2Source.java +++ /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 contributionPaths = Files.newDirectoryStream(base); - SortedSet 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 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 index a4c3ed5df..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/FsM2Source.java +++ /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 { - - @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 index 8630dc26d..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/OsgiContext.java +++ /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 index d8246f18d..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ProvisioningManager.java +++ /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 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 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 update() { - boolean fragmentsUpdated = false; - Set 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 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 index 7d6fadf7b..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/ProvisioningSource.java +++ /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 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 index b381410a4..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/a2/package-info.java +++ /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 index 516ab9080..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Activator.java +++ /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 - * http: - * //wiki.eclipse.org/Configurator - */ -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 index b0144a95c..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/AdminThread.java +++ /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 index 4a342fdbe..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/BundlesSet.java +++ /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 includes = new ArrayList(); - private List excludes = new ArrayList(); - - 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 getIncludes() { - return includes; - } - - public List 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 index a336b6446..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/DistributionBundle.java +++ /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 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 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 res) throws IOException { - try (DirectoryStream 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 listArtifacts(InputStream in) { - List osgiArtifacts = new ArrayList(); - 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 listUrls() { - if (baseUrl == null) - throw new OsgiBootException("Base URL is not set"); - - if (artifacts == null) - throw new OsgiBootException("Artifact list not initialized"); - - List urls = new ArrayList(); - 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 index f67f45b5b..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Launcher.java +++ /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 args = new ArrayList(); - 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 index 45094a7ee..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/Main.java +++ /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 index 263a4cda4..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/NodeRunner.java +++ /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 ff = ServiceLoader.load(FrameworkFactory.class); - FrameworkFactory frameworkFactory = ff.iterator().next(); - Map configuration = new HashMap(); - 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 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: "); - } -} 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 index b49e72001..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBoot.java +++ /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> duplicatePackages = diagnostics.findPackagesExportedTwice(); - if (duplicatePackages.size() > 0) { - OsgiBootUtils.info("Packages exported twice:"); - Iterator it = duplicatePackages.keySet().iterator(); - while (it.hasNext()) { - String pkgName = it.next(); - OsgiBootUtils.info(pkgName); - Set bdles = duplicatePackages.get(pkgName); - Iterator 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 urls = new ArrayList(); - urls.add(url); - installUrls(urls); - return (Bundle) getBundlesByLocation().get(url); - } - - /** Install the bundles at this URL list. */ - public void installUrls(List urls) { - Map 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 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> startLevels = new TreeMap>(); - computeStartLevels(startLevels, properties, defaultStartLevel); - // inverts the map for the time being, TODO optimise - Map 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> 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 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> startLevels, Integer startLevel, - String str) { - if (str == null || str.trim().equals("")) - return; - - if (!startLevels.containsKey(startLevel)) - startLevels.put(startLevel, new ArrayList()); - 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 bundlesToStart) { - if (bundlesToStart.size() == 0) - return true; - - // used to monitor ACTIVE states - List startedBundles = new ArrayList(); - // used to log the bundles not found - List notFoundBundles = new ArrayList(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 notStarted = new ArrayList(); - while (!allStarted && (System.currentTimeMillis() - beginMonitor) < defaultTimeout) { - notStarted = new ArrayList(); - 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:
- * /base/directory;in=*.jar;in=**;ex=org.eclipse.osgi_*;jar
- * WARNING: /base/directory;in=*.jar,\ at the end of a file, - * without a new line causes a '.' to be appended with unexpected side effects. - */ - public List 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 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 getBundlesUrls(String baseUrl, String bundlePatterns) { - List urls = new ArrayList(); - if (bundlePatterns == null) - return urls; - - bundlePatterns = SystemPropertyUtils.resolvePlaceholders(bundlePatterns); - if (OsgiBootUtils.debug) - debug(PROP_ARGEO_OSGI_BUNDLES + "=" + bundlePatterns); - - StringTokenizer st = new StringTokenizer(bundlePatterns, ","); - List bundlesSets = new ArrayList(); - 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 included = new ArrayList(); - 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 excluded = new ArrayList(); - 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 getDistributionUrls() { - String distributionUrl = getProperty(PROP_ARGEO_OSGI_DISTRIBUTION_URL); - String baseUrl = getProperty(PROP_ARGEO_OSGI_BASE_URL); - return getDistributionUrls(distributionUrl, baseUrl); - } - - public List getDistributionUrls(String distributionUrl, String baseUrl) { - List urls = new ArrayList(); - 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 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 getBundlesByLocation() { - Map installedBundles = new HashMap(); - 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 getBundlesBySymbolicName() { - Map namedBundles = new HashMap(); - 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 index e2d771920..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootConstants.java +++ /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 index 24a3317c6..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootDiagnostics.java +++ /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 packageAdminRef = bundleContext.getServiceReference(PackageAdmin.class); - PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(packageAdminRef); - packageAdmin.resolveBundles(null); - - Bundle[] bundles = bundleContext.getBundles(); - List unresolvedBundles = new ArrayList(); - 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> findPackagesExportedTwice() { - ServiceReference paSr = bundleContext.getServiceReference(PackageAdmin.class); - PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(paSr); - - // find packages exported twice - Bundle[] bundles = bundleContext.getBundles(); - Map> exportedPackages = new TreeMap>(); - 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()); - } - (exportedPackages.get(pkgName)).add(bundle.getSymbolicName() + "_" + bundle.getVersion()); - } - } - Map> duplicatePackages = new TreeMap>(); - Iterator it = exportedPackages.keySet().iterator(); - while (it.hasNext()) { - String pkgName = it.next().toString(); - Set 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 index fd6bf6546..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootException.java +++ /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 index c152ed74e..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBootUtils.java +++ /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, <0: tested version is newer, >0: - * currentVersion is newer. - */ - public static int compareVersions(String currentVersion, String testedVersion) { - List cToks = new ArrayList(); - StringTokenizer cSt = new StringTokenizer(currentVersion, "."); - while (cSt.hasMoreTokens()) - cToks.add(cSt.nextToken()); - List tToks = new ArrayList(); - 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 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 index 8c460e116..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/OsgiBuilder.java +++ /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 startLevels = new TreeMap<>(); - private List distributionBundles = new ArrayList<>(); - - private Map configuration = new HashMap(); - 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 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 lst = new ArrayList<>(); - Collections.addAll(lst, bundles.split(",")); - BundleTracker bt = new BundleTracker(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 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 bundles = new HashSet<>(); - - public void add(String bundle) { - String[] b = bundle.split(","); - Collections.addAll(bundles, b); - } - - public Set 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 index 55cd067b9..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/equinox/EquinoxUtils.java +++ /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 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 index b503752bf..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/equinox/package-info.java +++ /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 index e3fc6c299..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/AntPathMatcher.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright 2002-2007 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.argeo.osgi.boot.internal.springutil; - -/** - * PathMatcher implementation for Ant-style path patterns. - * Examples are provided below. - * - *

Part of this mapping code has been kindly borrowed from - * Apache Ant. - * - *

The mapping matches URLs using the following rules:
- *

    - *
  • ? matches one character
  • - *
  • * matches zero or more characters
  • - *
  • ** matches zero or more 'directories' in a path
  • - *
- * - *

Some examples:
- *

    - *
  • com/t?st.jsp - matches com/test.jsp but also - * com/tast.jsp or com/txst.jsp
  • - *
  • com/*.jsp - matches all .jsp files in the - * com directory
  • - *
  • com/**/test.jsp - matches all test.jsp - * files underneath the com path
  • - *
  • org/springframework/**/*.jsp - matches all .jsp - * files underneath the org/springframework path
  • - *
  • org/**/servlet/bla.jsp - matches - * org/springframework/servlet/bla.jsp but also - * org/springframework/testing/servlet/bla.jsp and - * org/servlet/bla.jsp
  • - *
- * - * @author Alef Arendsen - * @author Juergen Hoeller - * @author Rob Harrop - * @since 16.07.2003 - */ -public class AntPathMatcher implements PathMatcher { - - /** Default path separator: "/" */ - public static final String DEFAULT_PATH_SEPARATOR = "/"; - - private String pathSeparator = DEFAULT_PATH_SEPARATOR; - - - /** - * Set the path separator to use for pattern parsing. - * Default is "/", as in Ant. - */ - public void setPathSeparator(String pathSeparator) { - this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR); - } - - - public boolean isPattern(String path) { - return (path.indexOf('*') != -1 || path.indexOf('?') != -1); - } - - public boolean match(String pattern, String path) { - return doMatch(pattern, path, true); - } - - public boolean matchStart(String pattern, String path) { - return doMatch(pattern, path, false); - } - - - /** - * Actually match the given path against the given pattern. - * @param pattern the pattern to match against - * @param path the path String to test - * @param fullMatch whether a full pattern match is required - * (else a pattern match as far as the given base path goes is sufficient) - * @return true if the supplied path matched, - * false if it didn't - */ - protected boolean doMatch(String pattern, String path, boolean fullMatch) { - if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) { - return false; - } - - String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); - String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator); - - int pattIdxStart = 0; - int pattIdxEnd = pattDirs.length - 1; - int pathIdxStart = 0; - int pathIdxEnd = pathDirs.length - 1; - - // Match all elements up to the first ** - while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { - String patDir = pattDirs[pattIdxStart]; - if ("**".equals(patDir)) { - break; - } - if (!matchStrings(patDir, pathDirs[pathIdxStart])) { - return false; - } - pattIdxStart++; - pathIdxStart++; - } - - if (pathIdxStart > pathIdxEnd) { - // Path is exhausted, only match if rest of pattern is * or **'s - if (pattIdxStart > pattIdxEnd) { - return (pattern.endsWith(this.pathSeparator) ? - path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator)); - } - if (!fullMatch) { - return true; - } - if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && - path.endsWith(this.pathSeparator)) { - return true; - } - for (int i = pattIdxStart; i <= pattIdxEnd; i++) { - if (!pattDirs[i].equals("**")) { - return false; - } - } - return true; - } - else if (pattIdxStart > pattIdxEnd) { - // String not exhausted, but pattern is. Failure. - return false; - } - else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) { - // Path start definitely matches due to "**" part in pattern. - return true; - } - - // up to last '**' - while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { - String patDir = pattDirs[pattIdxEnd]; - if (patDir.equals("**")) { - break; - } - if (!matchStrings(patDir, pathDirs[pathIdxEnd])) { - return false; - } - pattIdxEnd--; - pathIdxEnd--; - } - if (pathIdxStart > pathIdxEnd) { - // String is exhausted - for (int i = pattIdxStart; i <= pattIdxEnd; i++) { - if (!pattDirs[i].equals("**")) { - return false; - } - } - return true; - } - - while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) { - int patIdxTmp = -1; - for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) { - if (pattDirs[i].equals("**")) { - patIdxTmp = i; - break; - } - } - if (patIdxTmp == pattIdxStart + 1) { - // '**/**' situation, so skip one - pattIdxStart++; - continue; - } - // Find the pattern between padIdxStart & padIdxTmp in str between - // strIdxStart & strIdxEnd - int patLength = (patIdxTmp - pattIdxStart - 1); - int strLength = (pathIdxEnd - pathIdxStart + 1); - int foundIdx = -1; - - strLoop: - for (int i = 0; i <= strLength - patLength; i++) { - for (int j = 0; j < patLength; j++) { - String subPat = (String) pattDirs[pattIdxStart + j + 1]; - String subStr = (String) pathDirs[pathIdxStart + i + j]; - if (!matchStrings(subPat, subStr)) { - continue strLoop; - } - } - foundIdx = pathIdxStart + i; - break; - } - - if (foundIdx == -1) { - return false; - } - - pattIdxStart = patIdxTmp; - pathIdxStart = foundIdx + patLength; - } - - for (int i = pattIdxStart; i <= pattIdxEnd; i++) { - if (!pattDirs[i].equals("**")) { - return false; - } - } - - return true; - } - - /** - * Tests whether or not a string matches against a pattern. - * The pattern may contain two special characters:
- * '*' means zero or more characters
- * '?' means one and only one character - * @param pattern pattern to match against. - * Must not be null. - * @param str string which must be matched against the pattern. - * Must not be null. - * @return true if the string matches against the - * pattern, or false otherwise. - */ - private boolean matchStrings(String pattern, String str) { - char[] patArr = pattern.toCharArray(); - char[] strArr = str.toCharArray(); - int patIdxStart = 0; - int patIdxEnd = patArr.length - 1; - int strIdxStart = 0; - int strIdxEnd = strArr.length - 1; - char ch; - - boolean containsStar = false; - for (int i = 0; i < patArr.length; i++) { - if (patArr[i] == '*') { - containsStar = true; - break; - } - } - - if (!containsStar) { - // No '*'s, so we make a shortcut - if (patIdxEnd != strIdxEnd) { - return false; // Pattern and string do not have the same size - } - for (int i = 0; i <= patIdxEnd; i++) { - ch = patArr[i]; - if (ch != '?') { - if (ch != strArr[i]) { - return false;// Character mismatch - } - } - } - return true; // String matches against pattern - } - - - if (patIdxEnd == 0) { - return true; // Pattern contains only '*', which matches anything - } - - // Process characters before first star - while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) { - if (ch != '?') { - if (ch != strArr[strIdxStart]) { - return false;// Character mismatch - } - } - patIdxStart++; - strIdxStart++; - } - if (strIdxStart > strIdxEnd) { - // All characters in the string are used. Check if only '*'s are - // left in the pattern. If so, we succeeded. Otherwise failure. - for (int i = patIdxStart; i <= patIdxEnd; i++) { - if (patArr[i] != '*') { - return false; - } - } - return true; - } - - // Process characters after last star - while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) { - if (ch != '?') { - if (ch != strArr[strIdxEnd]) { - return false;// Character mismatch - } - } - patIdxEnd--; - strIdxEnd--; - } - if (strIdxStart > strIdxEnd) { - // All characters in the string are used. Check if only '*'s are - // left in the pattern. If so, we succeeded. Otherwise failure. - for (int i = patIdxStart; i <= patIdxEnd; i++) { - if (patArr[i] != '*') { - return false; - } - } - return true; - } - - // process pattern between stars. padIdxStart and patIdxEnd point - // always to a '*'. - while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { - int patIdxTmp = -1; - for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { - if (patArr[i] == '*') { - patIdxTmp = i; - break; - } - } - if (patIdxTmp == patIdxStart + 1) { - // Two stars next to each other, skip the first one. - patIdxStart++; - continue; - } - // Find the pattern between padIdxStart & padIdxTmp in str between - // strIdxStart & strIdxEnd - int patLength = (patIdxTmp - patIdxStart - 1); - int strLength = (strIdxEnd - strIdxStart + 1); - int foundIdx = -1; - strLoop: - for (int i = 0; i <= strLength - patLength; i++) { - for (int j = 0; j < patLength; j++) { - ch = patArr[patIdxStart + j + 1]; - if (ch != '?') { - if (ch != strArr[strIdxStart + i + j]) { - continue strLoop; - } - } - } - - foundIdx = strIdxStart + i; - break; - } - - if (foundIdx == -1) { - return false; - } - - patIdxStart = patIdxTmp; - strIdxStart = foundIdx + patLength; - } - - // All characters in the string are used. Check if only '*'s are left - // in the pattern. If so, we succeeded. Otherwise failure. - for (int i = patIdxStart; i <= patIdxEnd; i++) { - if (patArr[i] != '*') { - return false; - } - } - - return true; - } - - /** - * Given a pattern and a full path, determine the pattern-mapped part. - *

For example: - *

    - *
  • '/docs/cvs/commit.html' and '/docs/cvs/commit.html to ''
  • - *
  • '/docs/*' and '/docs/cvs/commit to 'cvs/commit'
  • - *
  • '/docs/cvs/*.html' and '/docs/cvs/commit.html to 'commit.html'
  • - *
  • '/docs/**' and '/docs/cvs/commit to 'cvs/commit'
  • - *
  • '/docs/**\/*.html' and '/docs/cvs/commit.html to 'cvs/commit.html'
  • - *
  • '/*.html' and '/docs/cvs/commit.html to 'docs/cvs/commit.html'
  • - *
  • '*.html' and '/docs/cvs/commit.html to '/docs/cvs/commit.html'
  • - *
  • '*' and '/docs/cvs/commit.html to '/docs/cvs/commit.html'
  • - *
- *

Assumes that {@link #match} returns true for 'pattern' - * and 'path', but does not enforce this. - */ - public String extractPathWithinPattern(String pattern, String path) { - String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); - String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator); - - StringBuffer buffer = new StringBuffer(); - - // Add any path parts that have a wildcarded pattern part. - int puts = 0; - for (int i = 0; i < patternParts.length; i++) { - String patternPart = patternParts[i]; - if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) { - if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) { - buffer.append(this.pathSeparator); - } - buffer.append(pathParts[i]); - puts++; - } - } - - // Append any trailing path parts. - for (int i = patternParts.length; i < pathParts.length; i++) { - if (puts > 0 || i > 0) { - buffer.append(this.pathSeparator); - } - buffer.append(pathParts[i]); - } - - return buffer.toString(); - } - -} 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 index 2ac4562c8..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/Bootstrap.java +++ /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 props = new HashMap(); - 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 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 index 18cbe16ef..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/CollectionUtils.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2002-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.argeo.osgi.boot.internal.springutil; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -/** - * Miscellaneous collection utility methods. - * Mainly for internal use within the framework. - * - * @author Juergen Hoeller - * @author Rob Harrop - * @since 1.1.3 - */ -@SuppressWarnings({ "rawtypes", "unchecked" }) -public abstract class CollectionUtils { - - /** - * Return true if the supplied Collection is null - * or empty. Otherwise, return false. - * @param collection the Collection to check - * @return whether the given Collection is empty - */ - public static boolean isEmpty(Collection collection) { - return (collection == null || collection.isEmpty()); - } - - /** - * Return true if the supplied Map is null - * or empty. Otherwise, return false. - * @param map the Map to check - * @return whether the given Map is empty - */ - public static boolean isEmpty(Map map) { - return (map == null || map.isEmpty()); - } - - /** - * Convert the supplied array into a List. A primitive array gets - * converted into a List of the appropriate wrapper type. - *

A null source value will be converted to an - * empty List. - * @param source the (potentially primitive) array - * @return the converted List result - * @see ObjectUtils#toObjectArray(Object) - */ - public static List arrayToList(Object source) { - return Arrays.asList(ObjectUtils.toObjectArray(source)); - } - - /** - * Merge the given array into the given Collection. - * @param array the array to merge (may be null) - * @param collection the target Collection to merge the array into - */ - public static void mergeArrayIntoCollection(Object array, Collection collection) { - if (collection == null) { - throw new IllegalArgumentException("Collection must not be null"); - } - Object[] arr = ObjectUtils.toObjectArray(array); - for (int i = 0; i < arr.length; i++) { - collection.add(arr[i]); - } - } - - /** - * Merge the given Properties instance into the given Map, - * copying all properties (key-value pairs) over. - *

Uses Properties.propertyNames() to even catch - * default properties linked into the original Properties instance. - * @param props the Properties instance to merge (may be null) - * @param map the target Map to merge the properties into - */ - public static void mergePropertiesIntoMap(Properties props, Map map) { - if (map == null) { - throw new IllegalArgumentException("Map must not be null"); - } - if (props != null) { - for (Enumeration en = props.propertyNames(); en.hasMoreElements();) { - String key = (String) en.nextElement(); - map.put(key, props.getProperty(key)); - } - } - } - - - /** - * Check whether the given Iterator contains the given element. - * @param iterator the Iterator to check - * @param element the element to look for - * @return true if found, false else - */ - public static boolean contains(Iterator iterator, Object element) { - if (iterator != null) { - while (iterator.hasNext()) { - Object candidate = iterator.next(); - if (ObjectUtils.nullSafeEquals(candidate, element)) { - return true; - } - } - } - return false; - } - - /** - * Check whether the given Enumeration contains the given element. - * @param enumeration the Enumeration to check - * @param element the element to look for - * @return true if found, false else - */ - public static boolean contains(Enumeration enumeration, Object element) { - if (enumeration != null) { - while (enumeration.hasMoreElements()) { - Object candidate = enumeration.nextElement(); - if (ObjectUtils.nullSafeEquals(candidate, element)) { - return true; - } - } - } - return false; - } - - /** - * Check whether the given Collection contains the given element instance. - *

Enforces the given instance to be present, rather than returning - * true for an equal element as well. - * @param collection the Collection to check - * @param element the element to look for - * @return true if found, false else - */ - public static boolean containsInstance(Collection collection, Object element) { - if (collection != null) { - for (Iterator it = collection.iterator(); it.hasNext();) { - Object candidate = it.next(); - if (candidate == element) { - return true; - } - } - } - return false; - } - - /** - * Return true if any element in 'candidates' is - * contained in 'source'; otherwise returns false. - * @param source the source Collection - * @param candidates the candidates to search for - * @return whether any of the candidates has been found - */ - public static boolean containsAny(Collection source, Collection candidates) { - if (isEmpty(source) || isEmpty(candidates)) { - return false; - } - for (Iterator it = candidates.iterator(); it.hasNext();) { - if (source.contains(it.next())) { - return true; - } - } - return false; - } - - /** - * Return the first element in 'candidates' that is contained in - * 'source'. If no element in 'candidates' is present in - * 'source' returns null. Iteration order is - * {@link Collection} implementation specific. - * @param source the source Collection - * @param candidates the candidates to search for - * @return the first present object, or null if not found - */ - public static Object findFirstMatch(Collection source, Collection candidates) { - if (isEmpty(source) || isEmpty(candidates)) { - return null; - } - for (Iterator it = candidates.iterator(); it.hasNext();) { - Object candidate = it.next(); - if (source.contains(candidate)) { - return candidate; - } - } - return null; - } - - /** - * Find a single value of the given type in the given Collection. - * @param collection the Collection to search - * @param type the type to look for - * @return a value of the given type found if there is a clear match, - * or null if none or more than one such value found - */ - public static Object findValueOfType(Collection collection, Class type) { - if (isEmpty(collection)) { - return null; - } - Object value = null; - for (Iterator it = collection.iterator(); it.hasNext();) { - Object obj = it.next(); - if (type == null || type.isInstance(obj)) { - if (value != null) { - // More than one value found... no clear single value. - return null; - } - value = obj; - } - } - return value; - } - - /** - * Find a single value of one of the given types in the given Collection: - * searching the Collection for a value of the first type, then - * searching for a value of the second type, etc. - * @param collection the collection to search - * @param types the types to look for, in prioritized order - * @return a value of one of the given types found if there is a clear match, - * or null if none or more than one such value found - */ - public static Object findValueOfType(Collection collection, Class[] types) { - if (isEmpty(collection) || ObjectUtils.isEmpty(types)) { - return null; - } - for (int i = 0; i < types.length; i++) { - Object value = findValueOfType(collection, types[i]); - if (value != null) { - return value; - } - } - return null; - } - - /** - * Determine whether the given Collection only contains a single unique object. - * @param collection the Collection to check - * @return true if the collection contains a single reference or - * multiple references to the same instance, false else - */ - public static boolean hasUniqueObject(Collection collection) { - if (isEmpty(collection)) { - return false; - } - boolean hasCandidate = false; - Object candidate = null; - for (Iterator it = collection.iterator(); it.hasNext();) { - Object elem = it.next(); - if (!hasCandidate) { - hasCandidate = true; - candidate = elem; - } - else if (candidate != elem) { - return false; - } - } - return true; - } - -} 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 index 2c98b465c..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/ObjectUtils.java +++ /dev/null @@ -1,833 +0,0 @@ -/* - * Copyright 2002-2007 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.argeo.osgi.boot.internal.springutil; - -import java.lang.reflect.Array; -import java.util.Arrays; - -/** - * Miscellaneous object utility methods. Mainly for internal use within the - * framework; consider Jakarta's Commons Lang for a more comprehensive suite - * of object utilities. - * - * @author Juergen Hoeller - * @author Keith Donald - * @author Rod Johnson - * @author Rob Harrop - * @author Alex Ruiz - * @since 19.03.2004 - */ -@SuppressWarnings({ "rawtypes", "unchecked" }) -public abstract class ObjectUtils { - - private static final int INITIAL_HASH = 7; - private static final int MULTIPLIER = 31; - - private static final String EMPTY_STRING = ""; - private static final String NULL_STRING = "null"; - private static final String ARRAY_START = "{"; - private static final String ARRAY_END = "}"; - private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END; - private static final String ARRAY_ELEMENT_SEPARATOR = ", "; - - - /** - * Return whether the given throwable is a checked exception: - * that is, neither a RuntimeException nor an Error. - * @param ex the throwable to check - * @return whether the throwable is a checked exception - * @see java.lang.Exception - * @see java.lang.RuntimeException - * @see java.lang.Error - */ - public static boolean isCheckedException(Throwable ex) { - return !(ex instanceof RuntimeException || ex instanceof Error); - } - - /** - * Check whether the given exception is compatible with the exceptions - * declared in a throws clause. - * @param ex the exception to checked - * @param declaredExceptions the exceptions declared in the throws clause - * @return whether the given exception is compatible - */ - public static boolean isCompatibleWithThrowsClause(Throwable ex, Class[] declaredExceptions) { - if (!isCheckedException(ex)) { - return true; - } - if (declaredExceptions != null) { - for (int i = 0; i < declaredExceptions.length; i++) { - if (declaredExceptions[i].isAssignableFrom(ex.getClass())) { - return true; - } - } - } - return false; - } - - /** - * Return whether the given array is empty: that is, null - * or of zero length. - * @param array the array to check - * @return whether the given array is empty - */ - public static boolean isEmpty(Object[] array) { - return (array == null || array.length == 0); - } - - /** - * Check whether the given array contains the given element. - * @param array the array to check (may be null, - * in which case the return value will always be false) - * @param element the element to check for - * @return whether the element has been found in the given array - */ - public static boolean containsElement(Object[] array, Object element) { - if (array == null) { - return false; - } - for (int i = 0; i < array.length; i++) { - if (nullSafeEquals(array[i], element)) { - return true; - } - } - return false; - } - - /** - * Append the given Object to the given array, returning a new array - * consisting of the input array contents plus the given Object. - * @param array the array to append to (can be null) - * @param obj the Object to append - * @return the new array (of the same component type; never null) - */ - public static Object[] addObjectToArray(Object[] array, Object obj) { - Class compType = Object.class; - if (array != null) { - compType = array.getClass().getComponentType(); - } - else if (obj != null) { - compType = obj.getClass(); - } - int newArrLength = (array != null ? array.length + 1 : 1); - Object[] newArr = (Object[]) Array.newInstance(compType, newArrLength); - if (array != null) { - System.arraycopy(array, 0, newArr, 0, array.length); - } - newArr[newArr.length - 1] = obj; - return newArr; - } - - /** - * Convert the given array (which may be a primitive array) to an - * object array (if necessary of primitive wrapper objects). - *

A null source value will be converted to an - * empty Object array. - * @param source the (potentially primitive) array - * @return the corresponding object array (never null) - * @throws IllegalArgumentException if the parameter is not an array - */ - public static Object[] toObjectArray(Object source) { - if (source instanceof Object[]) { - return (Object[]) source; - } - if (source == null) { - return new Object[0]; - } - if (!source.getClass().isArray()) { - throw new IllegalArgumentException("Source is not an array: " + source); - } - int length = Array.getLength(source); - if (length == 0) { - return new Object[0]; - } - Class wrapperType = Array.get(source, 0).getClass(); - Object[] newArray = (Object[]) Array.newInstance(wrapperType, length); - for (int i = 0; i < length; i++) { - newArray[i] = Array.get(source, i); - } - return newArray; - } - - - //--------------------------------------------------------------------- - // Convenience methods for content-based equality/hash-code handling - //--------------------------------------------------------------------- - - /** - * Determine if the given objects are equal, returning true - * if both are null or false if only one is - * null. - *

Compares arrays with Arrays.equals, performing an equality - * check based on the array elements rather than the array reference. - * @param o1 first Object to compare - * @param o2 second Object to compare - * @return whether the given objects are equal - * @see java.util.Arrays#equals - */ - public static boolean nullSafeEquals(Object o1, Object o2) { - if (o1 == o2) { - return true; - } - if (o1 == null || o2 == null) { - return false; - } - if (o1.equals(o2)) { - return true; - } - if (o1.getClass().isArray() && o2.getClass().isArray()) { - if (o1 instanceof Object[] && o2 instanceof Object[]) { - return Arrays.equals((Object[]) o1, (Object[]) o2); - } - if (o1 instanceof boolean[] && o2 instanceof boolean[]) { - return Arrays.equals((boolean[]) o1, (boolean[]) o2); - } - if (o1 instanceof byte[] && o2 instanceof byte[]) { - return Arrays.equals((byte[]) o1, (byte[]) o2); - } - if (o1 instanceof char[] && o2 instanceof char[]) { - return Arrays.equals((char[]) o1, (char[]) o2); - } - if (o1 instanceof double[] && o2 instanceof double[]) { - return Arrays.equals((double[]) o1, (double[]) o2); - } - if (o1 instanceof float[] && o2 instanceof float[]) { - return Arrays.equals((float[]) o1, (float[]) o2); - } - if (o1 instanceof int[] && o2 instanceof int[]) { - return Arrays.equals((int[]) o1, (int[]) o2); - } - if (o1 instanceof long[] && o2 instanceof long[]) { - return Arrays.equals((long[]) o1, (long[]) o2); - } - if (o1 instanceof short[] && o2 instanceof short[]) { - return Arrays.equals((short[]) o1, (short[]) o2); - } - } - return false; - } - - /** - * Return as hash code for the given object; typically the value of - * {@link Object#hashCode()}. If the object is an array, - * this method will delegate to any of the nullSafeHashCode - * methods for arrays in this class. If the object is null, - * this method returns 0. - * @see #nullSafeHashCode(Object[]) - * @see #nullSafeHashCode(boolean[]) - * @see #nullSafeHashCode(byte[]) - * @see #nullSafeHashCode(char[]) - * @see #nullSafeHashCode(double[]) - * @see #nullSafeHashCode(float[]) - * @see #nullSafeHashCode(int[]) - * @see #nullSafeHashCode(long[]) - * @see #nullSafeHashCode(short[]) - */ - public static int nullSafeHashCode(Object obj) { - if (obj == null) { - return 0; - } - if (obj.getClass().isArray()) { - if (obj instanceof Object[]) { - return nullSafeHashCode((Object[]) obj); - } - if (obj instanceof boolean[]) { - return nullSafeHashCode((boolean[]) obj); - } - if (obj instanceof byte[]) { - return nullSafeHashCode((byte[]) obj); - } - if (obj instanceof char[]) { - return nullSafeHashCode((char[]) obj); - } - if (obj instanceof double[]) { - return nullSafeHashCode((double[]) obj); - } - if (obj instanceof float[]) { - return nullSafeHashCode((float[]) obj); - } - if (obj instanceof int[]) { - return nullSafeHashCode((int[]) obj); - } - if (obj instanceof long[]) { - return nullSafeHashCode((long[]) obj); - } - if (obj instanceof short[]) { - return nullSafeHashCode((short[]) obj); - } - } - return obj.hashCode(); - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(Object[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + nullSafeHashCode(array[i]); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(boolean[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + hashCode(array[i]); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(byte[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + array[i]; - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(char[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + array[i]; - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(double[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + hashCode(array[i]); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(float[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + hashCode(array[i]); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(int[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + array[i]; - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(long[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + hashCode(array[i]); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If array is null, this method returns 0. - */ - public static int nullSafeHashCode(short[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - int arraySize = array.length; - for (int i = 0; i < arraySize; i++) { - hash = MULTIPLIER * hash + array[i]; - } - return hash; - } - - /** - * Return the same value as {@link Boolean#hashCode()}. - * @see Boolean#hashCode() - */ - public static int hashCode(boolean bool) { - return bool ? 1231 : 1237; - } - - /** - * Return the same value as {@link Double#hashCode()}. - * @see Double#hashCode() - */ - public static int hashCode(double dbl) { - long bits = Double.doubleToLongBits(dbl); - return hashCode(bits); - } - - /** - * Return the same value as {@link Float#hashCode()}. - * @see Float#hashCode() - */ - public static int hashCode(float flt) { - return Float.floatToIntBits(flt); - } - - /** - * Return the same value as {@link Long#hashCode()}. - * @see Long#hashCode() - */ - public static int hashCode(long lng) { - return (int) (lng ^ (lng >>> 32)); - } - - - //--------------------------------------------------------------------- - // Convenience methods for toString output - //--------------------------------------------------------------------- - - /** - * Return a String representation of an object's overall identity. - * @param obj the object (may be null) - * @return the object's identity as String representation, - * or an empty String if the object was null - */ - public static String identityToString(Object obj) { - if (obj == null) { - return EMPTY_STRING; - } - return obj.getClass().getName() + "@" + getIdentityHexString(obj); - } - - /** - * Return a hex String form of an object's identity hash code. - * @param obj the object - * @return the object's identity code in hex notation - */ - public static String getIdentityHexString(Object obj) { - return Integer.toHexString(System.identityHashCode(obj)); - } - - /** - * Return a content-based String representation if obj is - * not null; otherwise returns an empty String. - *

Differs from {@link #nullSafeToString(Object)} in that it returns - * an empty String rather than "null" for a null value. - * @param obj the object to build a display String for - * @return a display String representation of obj - * @see #nullSafeToString(Object) - */ - public static String getDisplayString(Object obj) { - if (obj == null) { - return EMPTY_STRING; - } - return nullSafeToString(obj); - } - - /** - * Determine the class name for the given object. - *

Returns "null" if obj is null. - * @param obj the object to introspect (may be null) - * @return the corresponding class name - */ - public static String nullSafeClassName(Object obj) { - return (obj != null ? obj.getClass().getName() : NULL_STRING); - } - - /** - * Return a String representation of the specified Object. - *

Builds a String representation of the contents in case of an array. - * Returns "null" if obj is null. - * @param obj the object to build a String representation for - * @return a String representation of obj - */ - public static String nullSafeToString(Object obj) { - if (obj == null) { - return NULL_STRING; - } - if (obj instanceof String) { - return (String) obj; - } - if (obj instanceof Object[]) { - return nullSafeToString((Object[]) obj); - } - if (obj instanceof boolean[]) { - return nullSafeToString((boolean[]) obj); - } - if (obj instanceof byte[]) { - return nullSafeToString((byte[]) obj); - } - if (obj instanceof char[]) { - return nullSafeToString((char[]) obj); - } - if (obj instanceof double[]) { - return nullSafeToString((double[]) obj); - } - if (obj instanceof float[]) { - return nullSafeToString((float[]) obj); - } - if (obj instanceof int[]) { - return nullSafeToString((int[]) obj); - } - if (obj instanceof long[]) { - return nullSafeToString((long[]) obj); - } - if (obj instanceof short[]) { - return nullSafeToString((short[]) obj); - } - String str = obj.toString(); - return (str != null ? str : EMPTY_STRING); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(Object[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - buffer.append(String.valueOf(array[i])); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(boolean[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - - buffer.append(array[i]); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(byte[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - buffer.append(array[i]); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(char[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - buffer.append("'").append(array[i]).append("'"); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(double[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - - buffer.append(array[i]); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(float[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - - buffer.append(array[i]); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(int[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - buffer.append(array[i]); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(long[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - buffer.append(array[i]); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ("{}"). Adjacent elements are separated - * by the characters ", " (a comma followed by a space). Returns - * "null" if array is null. - * @param array the array to build a String representation for - * @return a String representation of array - */ - public static String nullSafeToString(short[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < length; i++) { - if (i == 0) { - buffer.append(ARRAY_START); - } - else { - buffer.append(ARRAY_ELEMENT_SEPARATOR); - } - buffer.append(array[i]); - } - buffer.append(ARRAY_END); - return buffer.toString(); - } - -} 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 index d7a23221b..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/PathMatcher.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2002-2007 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.argeo.osgi.boot.internal.springutil; - -/** - * Strategy interface for String-based path matching. - * - *

Used by {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}, - * {@link org.springframework.web.servlet.handler.AbstractUrlHandlerMapping}, - * {@link org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver}, - * and {@link org.springframework.web.servlet.mvc.WebContentInterceptor}. - * - *

The default implementation is {@link AntPathMatcher}, supporting the - * Ant-style pattern syntax. - * - * @author Juergen Hoeller - * @since 1.2 - * @see AntPathMatcher - */ -public interface PathMatcher { - - /** - * Does the given path represent a pattern that can be matched - * by an implementation of this interface? - *

If the return value is false, then the {@link #match} - * method does not have to be used because direct equality comparisons - * on the static path Strings will lead to the same result. - * @param path the path String to check - * @return true if the given path represents a pattern - */ - boolean isPattern(String path); - - /** - * Match the given path against the given pattern, - * according to this PathMatcher's matching strategy. - * @param pattern the pattern to match against - * @param path the path String to test - * @return true if the supplied path matched, - * false if it didn't - */ - boolean match(String pattern, String path); - - /** - * Match the given path against the corresponding part of the given - * pattern, according to this PathMatcher's matching strategy. - *

Determines whether the pattern at least matches as far as the given base - * path goes, assuming that a full path may then match as well. - * @param pattern the pattern to match against - * @param path the path String to test - * @return true if the supplied path matched, - * false if it didn't - */ - boolean matchStart(String pattern, String path); - - /** - * Given a pattern and a full path, determine the pattern-mapped part. - *

This method is supposed to find out which part of the path is matched - * dynamically through an actual pattern, that is, it strips off a statically - * defined leading path from the given full path, returning only the actually - * pattern-matched part of the path. - *

For example: For "myroot/*.html" as pattern and "myroot/myfile.html" - * as full path, this method should return "myfile.html". The detailed - * determination rules are specified to this PathMatcher's matching strategy. - *

A simple implementation may return the given full path as-is in case - * of an actual pattern, and the empty String in case of the pattern not - * containing any dynamic parts (i.e. the pattern parameter being - * a static path that wouldn't qualify as an actual {@link #isPattern pattern}). - * A sophisticated implementation will differentiate between the static parts - * and the dynamic parts of the given path pattern. - * @param pattern the path pattern - * @param path the full path to introspect - * @return the pattern-mapped part of the given path - * (never null) - */ - String extractPathWithinPattern(String pattern, String path); - -} 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 index 6cbaee8f8..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/StringUtils.java +++ /dev/null @@ -1,1113 +0,0 @@ -/* - * Copyright 2002-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.argeo.osgi.boot.internal.springutil; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Properties; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.TreeSet; - -/** - * Miscellaneous {@link String} utility methods. - * - *

Mainly for internal use within the framework; consider - * Jakarta's Commons Lang - * for a more comprehensive suite of String utilities. - * - *

This class delivers some simple functionality that should really - * be provided by the core Java String and {@link StringBuffer} - * classes, such as the ability to {@link #replace} all occurrences of a given - * substring in a target string. It also provides easy-to-use methods to convert - * between delimited strings, such as CSV strings, and collections and arrays. - * - * @author Rod Johnson - * @author Juergen Hoeller - * @author Keith Donald - * @author Rob Harrop - * @author Rick Evans - * @since 16 April 2001 - */ -@SuppressWarnings({ "rawtypes", "unchecked" }) -public abstract class StringUtils { - - private static final String FOLDER_SEPARATOR = "/"; - - private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; - - private static final String TOP_PATH = ".."; - - private static final String CURRENT_PATH = "."; - - private static final char EXTENSION_SEPARATOR = '.'; - - - //--------------------------------------------------------------------- - // General convenience methods for working with Strings - //--------------------------------------------------------------------- - - /** - * Check that the given CharSequence is neither null nor of length 0. - * Note: Will return true for a CharSequence that purely consists of whitespace. - *

-	 * StringUtils.hasLength(null) = false
-	 * StringUtils.hasLength("") = false
-	 * StringUtils.hasLength(" ") = true
-	 * StringUtils.hasLength("Hello") = true
-	 * 
- * @param str the CharSequence to check (may be null) - * @return true if the CharSequence is not null and has length - * @see #hasText(String) - */ - public static boolean hasLength(CharSequence str) { - return (str != null && str.length() > 0); - } - - /** - * Check that the given String is neither null nor of length 0. - * Note: Will return true for a String that purely consists of whitespace. - * @param str the String to check (may be null) - * @return true if the String is not null and has length - * @see #hasLength(CharSequence) - */ - public static boolean hasLength(String str) { - return hasLength((CharSequence) str); - } - - /** - * Check whether the given CharSequence has actual text. - * More specifically, returns true if the string not null, - * its length is greater than 0, and it contains at least one non-whitespace character. - *

-	 * StringUtils.hasText(null) = false
-	 * StringUtils.hasText("") = false
-	 * StringUtils.hasText(" ") = false
-	 * StringUtils.hasText("12345") = true
-	 * StringUtils.hasText(" 12345 ") = true
-	 * 
- * @param str the CharSequence to check (may be null) - * @return true if the CharSequence is not null, - * its length is greater than 0, and it does not contain whitespace only - * @see java.lang.Character#isWhitespace - */ - public static boolean hasText(CharSequence str) { - if (!hasLength(str)) { - return false; - } - int strLen = str.length(); - for (int i = 0; i < strLen; i++) { - if (!Character.isWhitespace(str.charAt(i))) { - return true; - } - } - return false; - } - - /** - * Check whether the given String has actual text. - * More specifically, returns true if the string not null, - * its length is greater than 0, and it contains at least one non-whitespace character. - * @param str the String to check (may be null) - * @return true if the String is not null, its length is - * greater than 0, and it does not contain whitespace only - * @see #hasText(CharSequence) - */ - public static boolean hasText(String str) { - return hasText((CharSequence) str); - } - - /** - * Check whether the given CharSequence contains any whitespace characters. - * @param str the CharSequence to check (may be null) - * @return true if the CharSequence is not empty and - * contains at least 1 whitespace character - * @see java.lang.Character#isWhitespace - */ - public static boolean containsWhitespace(CharSequence str) { - if (!hasLength(str)) { - return false; - } - int strLen = str.length(); - for (int i = 0; i < strLen; i++) { - if (Character.isWhitespace(str.charAt(i))) { - return true; - } - } - return false; - } - - /** - * Check whether the given String contains any whitespace characters. - * @param str the String to check (may be null) - * @return true if the String is not empty and - * contains at least 1 whitespace character - * @see #containsWhitespace(CharSequence) - */ - public static boolean containsWhitespace(String str) { - return containsWhitespace((CharSequence) str); - } - - /** - * Trim leading and trailing whitespace from the given String. - * @param str the String to check - * @return the trimmed String - * @see java.lang.Character#isWhitespace - */ - public static String trimWhitespace(String str) { - if (!hasLength(str)) { - return str; - } - StringBuffer buf = new StringBuffer(str); - while (buf.length() > 0 && Character.isWhitespace(buf.charAt(0))) { - buf.deleteCharAt(0); - } - while (buf.length() > 0 && Character.isWhitespace(buf.charAt(buf.length() - 1))) { - buf.deleteCharAt(buf.length() - 1); - } - return buf.toString(); - } - - /** - * Trim all whitespace from the given String: - * leading, trailing, and inbetween characters. - * @param str the String to check - * @return the trimmed String - * @see java.lang.Character#isWhitespace - */ - public static String trimAllWhitespace(String str) { - if (!hasLength(str)) { - return str; - } - StringBuffer buf = new StringBuffer(str); - int index = 0; - while (buf.length() > index) { - if (Character.isWhitespace(buf.charAt(index))) { - buf.deleteCharAt(index); - } - else { - index++; - } - } - return buf.toString(); - } - - /** - * Trim leading whitespace from the given String. - * @param str the String to check - * @return the trimmed String - * @see java.lang.Character#isWhitespace - */ - public static String trimLeadingWhitespace(String str) { - if (!hasLength(str)) { - return str; - } - StringBuffer buf = new StringBuffer(str); - while (buf.length() > 0 && Character.isWhitespace(buf.charAt(0))) { - buf.deleteCharAt(0); - } - return buf.toString(); - } - - /** - * Trim trailing whitespace from the given String. - * @param str the String to check - * @return the trimmed String - * @see java.lang.Character#isWhitespace - */ - public static String trimTrailingWhitespace(String str) { - if (!hasLength(str)) { - return str; - } - StringBuffer buf = new StringBuffer(str); - while (buf.length() > 0 && Character.isWhitespace(buf.charAt(buf.length() - 1))) { - buf.deleteCharAt(buf.length() - 1); - } - return buf.toString(); - } - - /** - * Trim all occurences of the supplied leading character from the given String. - * @param str the String to check - * @param leadingCharacter the leading character to be trimmed - * @return the trimmed String - */ - public static String trimLeadingCharacter(String str, char leadingCharacter) { - if (!hasLength(str)) { - return str; - } - StringBuffer buf = new StringBuffer(str); - while (buf.length() > 0 && buf.charAt(0) == leadingCharacter) { - buf.deleteCharAt(0); - } - return buf.toString(); - } - - /** - * Trim all occurences of the supplied trailing character from the given String. - * @param str the String to check - * @param trailingCharacter the trailing character to be trimmed - * @return the trimmed String - */ - public static String trimTrailingCharacter(String str, char trailingCharacter) { - if (!hasLength(str)) { - return str; - } - StringBuffer buf = new StringBuffer(str); - while (buf.length() > 0 && buf.charAt(buf.length() - 1) == trailingCharacter) { - buf.deleteCharAt(buf.length() - 1); - } - return buf.toString(); - } - - - /** - * Test if the given String starts with the specified prefix, - * ignoring upper/lower case. - * @param str the String to check - * @param prefix the prefix to look for - * @see java.lang.String#startsWith - */ - public static boolean startsWithIgnoreCase(String str, String prefix) { - if (str == null || prefix == null) { - return false; - } - if (str.startsWith(prefix)) { - return true; - } - if (str.length() < prefix.length()) { - return false; - } - String lcStr = str.substring(0, prefix.length()).toLowerCase(); - String lcPrefix = prefix.toLowerCase(); - return lcStr.equals(lcPrefix); - } - - /** - * Test if the given String ends with the specified suffix, - * ignoring upper/lower case. - * @param str the String to check - * @param suffix the suffix to look for - * @see java.lang.String#endsWith - */ - public static boolean endsWithIgnoreCase(String str, String suffix) { - if (str == null || suffix == null) { - return false; - } - if (str.endsWith(suffix)) { - return true; - } - if (str.length() < suffix.length()) { - return false; - } - - String lcStr = str.substring(str.length() - suffix.length()).toLowerCase(); - String lcSuffix = suffix.toLowerCase(); - return lcStr.equals(lcSuffix); - } - - /** - * Test whether the given string matches the given substring - * at the given index. - * @param str the original string (or StringBuffer) - * @param index the index in the original string to start matching against - * @param substring the substring to match at the given index - */ - public static boolean substringMatch(CharSequence str, int index, CharSequence substring) { - for (int j = 0; j < substring.length(); j++) { - int i = index + j; - if (i >= str.length() || str.charAt(i) != substring.charAt(j)) { - return false; - } - } - return true; - } - - /** - * Count the occurrences of the substring in string s. - * @param str string to search in. Return 0 if this is null. - * @param sub string to search for. Return 0 if this is null. - */ - public static int countOccurrencesOf(String str, String sub) { - if (str == null || sub == null || str.length() == 0 || sub.length() == 0) { - return 0; - } - int count = 0, pos = 0, idx = 0; - while ((idx = str.indexOf(sub, pos)) != -1) { - ++count; - pos = idx + sub.length(); - } - return count; - } - - /** - * Replace all occurences of a substring within a string with - * another string. - * @param inString String to examine - * @param oldPattern String to replace - * @param newPattern String to insert - * @return a String with the replacements - */ - public static String replace(String inString, String oldPattern, String newPattern) { - if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { - return inString; - } - StringBuffer sbuf = new StringBuffer(); - // output StringBuffer we'll build up - int pos = 0; // our position in the old string - int index = inString.indexOf(oldPattern); - // the index of an occurrence we've found, or -1 - int patLen = oldPattern.length(); - while (index >= 0) { - sbuf.append(inString.substring(pos, index)); - sbuf.append(newPattern); - pos = index + patLen; - index = inString.indexOf(oldPattern, pos); - } - sbuf.append(inString.substring(pos)); - // remember to append any characters to the right of a match - return sbuf.toString(); - } - - /** - * Delete all occurrences of the given substring. - * @param inString the original String - * @param pattern the pattern to delete all occurrences of - * @return the resulting String - */ - public static String delete(String inString, String pattern) { - return replace(inString, pattern, ""); - } - - /** - * Delete any character in a given String. - * @param inString the original String - * @param charsToDelete a set of characters to delete. - * E.g. "az\n" will delete 'a's, 'z's and new lines. - * @return the resulting String - */ - public static String deleteAny(String inString, String charsToDelete) { - if (!hasLength(inString) || !hasLength(charsToDelete)) { - return inString; - } - StringBuffer out = new StringBuffer(); - for (int i = 0; i < inString.length(); i++) { - char c = inString.charAt(i); - if (charsToDelete.indexOf(c) == -1) { - out.append(c); - } - } - return out.toString(); - } - - - //--------------------------------------------------------------------- - // Convenience methods for working with formatted Strings - //--------------------------------------------------------------------- - - /** - * Quote the given String with single quotes. - * @param str the input String (e.g. "myString") - * @return the quoted String (e.g. "'myString'"), - * or null if the input was null - */ - public static String quote(String str) { - return (str != null ? "'" + str + "'" : null); - } - - /** - * Turn the given Object into a String with single quotes - * if it is a String; keeping the Object as-is else. - * @param obj the input Object (e.g. "myString") - * @return the quoted String (e.g. "'myString'"), - * or the input object as-is if not a String - */ - public static Object quoteIfString(Object obj) { - return (obj instanceof String ? quote((String) obj) : obj); - } - - /** - * Unqualify a string qualified by a '.' dot character. For example, - * "this.name.is.qualified", returns "qualified". - * @param qualifiedName the qualified name - */ - public static String unqualify(String qualifiedName) { - return unqualify(qualifiedName, '.'); - } - - /** - * Unqualify a string qualified by a separator character. For example, - * "this:name:is:qualified" returns "qualified" if using a ':' separator. - * @param qualifiedName the qualified name - * @param separator the separator - */ - public static String unqualify(String qualifiedName, char separator) { - return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1); - } - - /** - * Capitalize a String, changing the first letter to - * upper case as per {@link Character#toUpperCase(char)}. - * No other letters are changed. - * @param str the String to capitalize, may be null - * @return the capitalized String, null if null - */ - public static String capitalize(String str) { - return changeFirstCharacterCase(str, true); - } - - /** - * Uncapitalize a String, changing the first letter to - * lower case as per {@link Character#toLowerCase(char)}. - * No other letters are changed. - * @param str the String to uncapitalize, may be null - * @return the uncapitalized String, null if null - */ - public static String uncapitalize(String str) { - return changeFirstCharacterCase(str, false); - } - - private static String changeFirstCharacterCase(String str, boolean capitalize) { - if (str == null || str.length() == 0) { - return str; - } - StringBuffer buf = new StringBuffer(str.length()); - if (capitalize) { - buf.append(Character.toUpperCase(str.charAt(0))); - } - else { - buf.append(Character.toLowerCase(str.charAt(0))); - } - buf.append(str.substring(1)); - return buf.toString(); - } - - /** - * Extract the filename from the given path, - * e.g. "mypath/myfile.txt" to "myfile.txt". - * @param path the file path (may be null) - * @return the extracted filename, or null if none - */ - public static String getFilename(String path) { - if (path == null) { - return null; - } - int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); - return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path); - } - - /** - * Extract the filename extension from the given path, - * e.g. "mypath/myfile.txt" to "txt". - * @param path the file path (may be null) - * @return the extracted filename extension, or null if none - */ - public static String getFilenameExtension(String path) { - if (path == null) { - return null; - } - int sepIndex = path.lastIndexOf(EXTENSION_SEPARATOR); - return (sepIndex != -1 ? path.substring(sepIndex + 1) : null); - } - - /** - * Strip the filename extension from the given path, - * e.g. "mypath/myfile.txt" to "mypath/myfile". - * @param path the file path (may be null) - * @return the path with stripped filename extension, - * or null if none - */ - public static String stripFilenameExtension(String path) { - if (path == null) { - return null; - } - int sepIndex = path.lastIndexOf(EXTENSION_SEPARATOR); - return (sepIndex != -1 ? path.substring(0, sepIndex) : path); - } - - /** - * Apply the given relative path to the given path, - * assuming standard Java folder separation (i.e. "/" separators); - * @param path the path to start from (usually a full file path) - * @param relativePath the relative path to apply - * (relative to the full file path above) - * @return the full file path that results from applying the relative path - */ - public static String applyRelativePath(String path, String relativePath) { - int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); - if (separatorIndex != -1) { - String newPath = path.substring(0, separatorIndex); - if (!relativePath.startsWith(FOLDER_SEPARATOR)) { - newPath += FOLDER_SEPARATOR; - } - return newPath + relativePath; - } - else { - return relativePath; - } - } - - /** - * Normalize the path by suppressing sequences like "path/.." and - * inner simple dots. - *

The result is convenient for path comparison. For other uses, - * notice that Windows separators ("\") are replaced by simple slashes. - * @param path the original path - * @return the normalized path - */ - public static String cleanPath(String path) { - if (path == null) { - return null; - } - String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); - - // Strip prefix from path to analyze, to not treat it as part of the - // first path element. This is necessary to correctly parse paths like - // "file:core/../core/io/Resource.class", where the ".." should just - // strip the first "core" directory while keeping the "file:" prefix. - int prefixIndex = pathToUse.indexOf(":"); - String prefix = ""; - if (prefixIndex != -1) { - prefix = pathToUse.substring(0, prefixIndex + 1); - pathToUse = pathToUse.substring(prefixIndex + 1); - } - if (pathToUse.startsWith(FOLDER_SEPARATOR)) { - prefix = prefix + FOLDER_SEPARATOR; - pathToUse = pathToUse.substring(1); - } - - String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); - List pathElements = new LinkedList(); - int tops = 0; - - for (int i = pathArray.length - 1; i >= 0; i--) { - String element = pathArray[i]; - if (CURRENT_PATH.equals(element)) { - // Points to current directory - drop it. - } - else if (TOP_PATH.equals(element)) { - // Registering top path found. - tops++; - } - else { - if (tops > 0) { - // Merging path element with element corresponding to top path. - tops--; - } - else { - // Normal path element found. - pathElements.add(0, element); - } - } - } - - // Remaining top paths need to be retained. - for (int i = 0; i < tops; i++) { - pathElements.add(0, TOP_PATH); - } - - return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); - } - - /** - * Compare two paths after normalization of them. - * @param path1 first path for comparison - * @param path2 second path for comparison - * @return whether the two paths are equivalent after normalization - */ - public static boolean pathEquals(String path1, String path2) { - return cleanPath(path1).equals(cleanPath(path2)); - } - - /** - * Parse the given localeString into a {@link Locale}. - *

This is the inverse operation of {@link Locale#toString Locale's toString}. - * @param localeString the locale string, following Locale's - * toString() format ("en", "en_UK", etc); - * also accepts spaces as separators, as an alternative to underscores - * @return a corresponding Locale instance - */ - public static Locale parseLocaleString(String localeString) { - String[] parts = tokenizeToStringArray(localeString, "_ ", false, false); - String language = (parts.length > 0 ? parts[0] : ""); - String country = (parts.length > 1 ? parts[1] : ""); - String variant = ""; - if (parts.length >= 2) { - // There is definitely a variant, and it is everything after the country - // code sans the separator between the country code and the variant. - int endIndexOfCountryCode = localeString.indexOf(country) + country.length(); - // Strip off any leading '_' and whitespace, what's left is the variant. - variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode)); - if (variant.startsWith("_")) { - variant = trimLeadingCharacter(variant, '_'); - } - } - return (language.length() > 0 ? new Locale(language, country, variant) : null); - } - - /** - * Determine the RFC 3066 compliant language tag, - * as used for the HTTP "Accept-Language" header. - * @param locale the Locale to transform to a language tag - * @return the RFC 3066 compliant language tag as String - */ - public static String toLanguageTag(Locale locale) { - return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : ""); - } - - - //--------------------------------------------------------------------- - // Convenience methods for working with String arrays - //--------------------------------------------------------------------- - - /** - * Append the given String to the given String array, returning a new array - * consisting of the input array contents plus the given String. - * @param array the array to append to (can be null) - * @param str the String to append - * @return the new array (never null) - */ - public static String[] addStringToArray(String[] array, String str) { - if (ObjectUtils.isEmpty(array)) { - return new String[] {str}; - } - String[] newArr = new String[array.length + 1]; - System.arraycopy(array, 0, newArr, 0, array.length); - newArr[array.length] = str; - return newArr; - } - - /** - * Concatenate the given String arrays into one, - * with overlapping array elements included twice. - *

The order of elements in the original arrays is preserved. - * @param array1 the first array (can be null) - * @param array2 the second array (can be null) - * @return the new array (null if both given arrays were null) - */ - public static String[] concatenateStringArrays(String[] array1, String[] array2) { - if (ObjectUtils.isEmpty(array1)) { - return array2; - } - if (ObjectUtils.isEmpty(array2)) { - return array1; - } - String[] newArr = new String[array1.length + array2.length]; - System.arraycopy(array1, 0, newArr, 0, array1.length); - System.arraycopy(array2, 0, newArr, array1.length, array2.length); - return newArr; - } - - /** - * Merge the given String arrays into one, with overlapping - * array elements only included once. - *

The order of elements in the original arrays is preserved - * (with the exception of overlapping elements, which are only - * included on their first occurence). - * @param array1 the first array (can be null) - * @param array2 the second array (can be null) - * @return the new array (null if both given arrays were null) - */ - public static String[] mergeStringArrays(String[] array1, String[] array2) { - if (ObjectUtils.isEmpty(array1)) { - return array2; - } - if (ObjectUtils.isEmpty(array2)) { - return array1; - } - List result = new ArrayList(); - result.addAll(Arrays.asList(array1)); - for (int i = 0; i < array2.length; i++) { - String str = array2[i]; - if (!result.contains(str)) { - result.add(str); - } - } - return toStringArray(result); - } - - /** - * Turn given source String array into sorted array. - * @param array the source array - * @return the sorted array (never null) - */ - public static String[] sortStringArray(String[] array) { - if (ObjectUtils.isEmpty(array)) { - return new String[0]; - } - Arrays.sort(array); - return array; - } - - /** - * Copy the given Collection into a String array. - * The Collection must contain String elements only. - * @param collection the Collection to copy - * @return the String array (null if the passed-in - * Collection was null) - */ - public static String[] toStringArray(Collection collection) { - if (collection == null) { - return null; - } - return (String[]) collection.toArray(new String[collection.size()]); - } - - /** - * Copy the given Enumeration into a String array. - * The Enumeration must contain String elements only. - * @param enumeration the Enumeration to copy - * @return the String array (null if the passed-in - * Enumeration was null) - */ - public static String[] toStringArray(Enumeration enumeration) { - if (enumeration == null) { - return null; - } - List list = Collections.list(enumeration); - return (String[]) list.toArray(new String[list.size()]); - } - - /** - * Trim the elements of the given String array, - * calling String.trim() on each of them. - * @param array the original String array - * @return the resulting array (of the same size) with trimmed elements - */ - public static String[] trimArrayElements(String[] array) { - if (ObjectUtils.isEmpty(array)) { - return new String[0]; - } - String[] result = new String[array.length]; - for (int i = 0; i < array.length; i++) { - String element = array[i]; - result[i] = (element != null ? element.trim() : null); - } - return result; - } - - /** - * Remove duplicate Strings from the given array. - * Also sorts the array, as it uses a TreeSet. - * @param array the String array - * @return an array without duplicates, in natural sort order - */ - public static String[] removeDuplicateStrings(String[] array) { - if (ObjectUtils.isEmpty(array)) { - return array; - } - Set set = new TreeSet(); - for (int i = 0; i < array.length; i++) { - set.add(array[i]); - } - return toStringArray(set); - } - - /** - * Split a String at the first occurrence of the delimiter. - * Does not include the delimiter in the result. - * @param toSplit the string to split - * @param delimiter to split the string up with - * @return a two element array with index 0 being before the delimiter, and - * index 1 being after the delimiter (neither element includes the delimiter); - * or null if the delimiter wasn't found in the given input String - */ - public static String[] split(String toSplit, String delimiter) { - if (!hasLength(toSplit) || !hasLength(delimiter)) { - return null; - } - int offset = toSplit.indexOf(delimiter); - if (offset < 0) { - return null; - } - String beforeDelimiter = toSplit.substring(0, offset); - String afterDelimiter = toSplit.substring(offset + delimiter.length()); - return new String[] {beforeDelimiter, afterDelimiter}; - } - - /** - * Take an array Strings and split each element based on the given delimiter. - * A Properties instance is then generated, with the left of the - * delimiter providing the key, and the right of the delimiter providing the value. - *

Will trim both the key and value before adding them to the - * Properties instance. - * @param array the array to process - * @param delimiter to split each element using (typically the equals symbol) - * @return a Properties instance representing the array contents, - * or null if the array to process was null or empty - */ - public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) { - return splitArrayElementsIntoProperties(array, delimiter, null); - } - - /** - * Take an array Strings and split each element based on the given delimiter. - * A Properties instance is then generated, with the left of the - * delimiter providing the key, and the right of the delimiter providing the value. - *

Will trim both the key and value before adding them to the - * Properties instance. - * @param array the array to process - * @param delimiter to split each element using (typically the equals symbol) - * @param charsToDelete one or more characters to remove from each element - * prior to attempting the split operation (typically the quotation mark - * symbol), or null if no removal should occur - * @return a Properties instance representing the array contents, - * or null if the array to process was null or empty - */ - public static Properties splitArrayElementsIntoProperties( - String[] array, String delimiter, String charsToDelete) { - - if (ObjectUtils.isEmpty(array)) { - return null; - } - Properties result = new Properties(); - for (int i = 0; i < array.length; i++) { - String element = array[i]; - if (charsToDelete != null) { - element = deleteAny(array[i], charsToDelete); - } - String[] splittedElement = split(element, delimiter); - if (splittedElement == null) { - continue; - } - result.setProperty(splittedElement[0].trim(), splittedElement[1].trim()); - } - return result; - } - - /** - * Tokenize the given String into a String array via a StringTokenizer. - * Trims tokens and omits empty tokens. - *

The given delimiters string is supposed to consist of any number of - * delimiter characters. Each of those characters can be used to separate - * tokens. A delimiter is always a single character; for multi-character - * delimiters, consider using delimitedListToStringArray - * @param str the String to tokenize - * @param delimiters the delimiter characters, assembled as String - * (each of those characters is individually considered as delimiter). - * @return an array of the tokens - * @see java.util.StringTokenizer - * @see java.lang.String#trim() - * @see #delimitedListToStringArray - */ - public static String[] tokenizeToStringArray(String str, String delimiters) { - return tokenizeToStringArray(str, delimiters, true, true); - } - - /** - * Tokenize the given String into a String array via a StringTokenizer. - *

The given delimiters string is supposed to consist of any number of - * delimiter characters. Each of those characters can be used to separate - * tokens. A delimiter is always a single character; for multi-character - * delimiters, consider using delimitedListToStringArray - * @param str the String to tokenize - * @param delimiters the delimiter characters, assembled as String - * (each of those characters is individually considered as delimiter) - * @param trimTokens trim the tokens via String's trim - * @param ignoreEmptyTokens omit empty tokens from the result array - * (only applies to tokens that are empty after trimming; StringTokenizer - * will not consider subsequent delimiters as token in the first place). - * @return an array of the tokens (null if the input String - * was null) - * @see java.util.StringTokenizer - * @see java.lang.String#trim() - * @see #delimitedListToStringArray - */ - public static String[] tokenizeToStringArray( - String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { - - if (str == null) { - return null; - } - StringTokenizer st = new StringTokenizer(str, delimiters); - List tokens = new ArrayList(); - while (st.hasMoreTokens()) { - String token = st.nextToken(); - if (trimTokens) { - token = token.trim(); - } - if (!ignoreEmptyTokens || token.length() > 0) { - tokens.add(token); - } - } - return toStringArray(tokens); - } - - /** - * Take a String which is a delimited list and convert it to a String array. - *

A single delimiter can consists of more than one character: It will still - * be considered as single delimiter string, rather than as bunch of potential - * delimiter characters - in contrast to tokenizeToStringArray. - * @param str the input String - * @param delimiter the delimiter between elements (this is a single delimiter, - * rather than a bunch individual delimiter characters) - * @return an array of the tokens in the list - * @see #tokenizeToStringArray - */ - public static String[] delimitedListToStringArray(String str, String delimiter) { - return delimitedListToStringArray(str, delimiter, null); - } - - /** - * Take a String which is a delimited list and convert it to a String array. - *

A single delimiter can consists of more than one character: It will still - * be considered as single delimiter string, rather than as bunch of potential - * delimiter characters - in contrast to tokenizeToStringArray. - * @param str the input String - * @param delimiter the delimiter between elements (this is a single delimiter, - * rather than a bunch individual delimiter characters) - * @param charsToDelete a set of characters to delete. Useful for deleting unwanted - * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a String. - * @return an array of the tokens in the list - * @see #tokenizeToStringArray - */ - public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { - if (str == null) { - return new String[0]; - } - if (delimiter == null) { - return new String[] {str}; - } - List result = new ArrayList(); - if ("".equals(delimiter)) { - for (int i = 0; i < str.length(); i++) { - result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); - } - } - else { - int pos = 0; - int delPos = 0; - while ((delPos = str.indexOf(delimiter, pos)) != -1) { - result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); - pos = delPos + delimiter.length(); - } - if (str.length() > 0 && pos <= str.length()) { - // Add rest of String, but not in case of empty input. - result.add(deleteAny(str.substring(pos), charsToDelete)); - } - } - return toStringArray(result); - } - - /** - * Convert a CSV list into an array of Strings. - * @param str the input String - * @return an array of Strings, or the empty array in case of empty input - */ - public static String[] commaDelimitedListToStringArray(String str) { - return delimitedListToStringArray(str, ","); - } - - /** - * Convenience method to convert a CSV string list to a set. - * Note that this will suppress duplicates. - * @param str the input String - * @return a Set of String entries in the list - */ - public static Set commaDelimitedListToSet(String str) { - Set set = new TreeSet(); - String[] tokens = commaDelimitedListToStringArray(str); - for (int i = 0; i < tokens.length; i++) { - set.add(tokens[i]); - } - return set; - } - - /** - * Convenience method to return a Collection as a delimited (e.g. CSV) - * String. E.g. useful for toString() implementations. - * @param coll the Collection to display - * @param delim the delimiter to use (probably a ",") - * @param prefix the String to start each element with - * @param suffix the String to end each element with - * @return the delimited String - */ - public static String collectionToDelimitedString(Collection coll, String delim, String prefix, String suffix) { - if (CollectionUtils.isEmpty(coll)) { - return ""; - } - StringBuffer sb = new StringBuffer(); - Iterator it = coll.iterator(); - while (it.hasNext()) { - sb.append(prefix).append(it.next()).append(suffix); - if (it.hasNext()) { - sb.append(delim); - } - } - return sb.toString(); - } - - /** - * Convenience method to return a Collection as a delimited (e.g. CSV) - * String. E.g. useful for toString() implementations. - * @param coll the Collection to display - * @param delim the delimiter to use (probably a ",") - * @return the delimited String - */ - public static String collectionToDelimitedString(Collection coll, String delim) { - return collectionToDelimitedString(coll, delim, "", ""); - } - - /** - * Convenience method to return a Collection as a CSV String. - * E.g. useful for toString() implementations. - * @param coll the Collection to display - * @return the delimited String - */ - public static String collectionToCommaDelimitedString(Collection coll) { - return collectionToDelimitedString(coll, ","); - } - - /** - * Convenience method to return a String array as a delimited (e.g. CSV) - * String. E.g. useful for toString() implementations. - * @param arr the array to display - * @param delim the delimiter to use (probably a ",") - * @return the delimited String - */ - public static String arrayToDelimitedString(Object[] arr, String delim) { - if (ObjectUtils.isEmpty(arr)) { - return ""; - } - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < arr.length; i++) { - if (i > 0) { - sb.append(delim); - } - sb.append(arr[i]); - } - return sb.toString(); - } - - /** - * Convenience method to return a String array as a CSV String. - * E.g. useful for toString() implementations. - * @param arr the array to display - * @return the delimited String - */ - public static String arrayToCommaDelimitedString(Object[] arr) { - return arrayToDelimitedString(arr, ","); - } - -} 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 index ff81a2219..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/internal/springutil/SystemPropertyUtils.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2002-2008 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.argeo.osgi.boot.internal.springutil; - -/** - * Helper class for resolving placeholders in texts. Usually applied to file paths. - * - *

A text may contain ${...} placeholders, to be resolved as - * system properties: e.g. ${user.dir}. - * - * @author Juergen Hoeller - * @since 1.2.5 - * @see #PLACEHOLDER_PREFIX - * @see #PLACEHOLDER_SUFFIX - * @see System#getProperty(String) - */ -public abstract class SystemPropertyUtils { - - /** Prefix for system property placeholders: "${" */ - public static final String PLACEHOLDER_PREFIX = "${"; - - /** Suffix for system property placeholders: "}" */ - public static final String PLACEHOLDER_SUFFIX = "}"; - - - /** - * Resolve ${...} placeholders in the given text, - * replacing them with corresponding system property values. - * @param text the String to resolve - * @return the resolved String - * @see #PLACEHOLDER_PREFIX - * @see #PLACEHOLDER_SUFFIX - */ - @SuppressWarnings("unused") - public static String resolvePlaceholders(String text) { - StringBuffer buf = new StringBuffer(text); - - int startIndex = buf.indexOf(PLACEHOLDER_PREFIX); - while (startIndex != -1) { - int endIndex = buf.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length()); - if (endIndex != -1) { - String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex); - int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length(); - try { - String propVal = System.getProperty(placeholder); - if (propVal == null) { - // Fall back to searching the system environment. - //propVal = System.getenv(placeholder);// mbaudier - 2009-07-26 - throw new Error("getenv no longer supported, use properties and -D instead: " + placeholder); - } - if (propVal != null) { - buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), propVal); - nextIndex = startIndex + propVal.length(); - } - else { - System.err.println("Could not resolve placeholder '" + placeholder + "' in [" + text + - "] as system property: neither system property nor environment variable found"); - } - } - catch (Throwable ex) { - System.err.println("Could not resolve placeholder '" + placeholder + "' in [" + text + - "] as system property: " + ex); - } - startIndex = buf.indexOf(PLACEHOLDER_PREFIX, nextIndex); - } - else { - startIndex = -1; - } - } - - return buf.toString(); - } - -} 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 index 1fcf25e3b..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/log4j.properties +++ /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 index facb61327..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/node.policy +++ /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 index 7474d13bf..000000000 --- a/org.argeo.osgi.boot/src/org/argeo/osgi/boot/package-info.java +++ /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 2721905f8..d199b7ec1 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ - org.argeo.osgi.boot + org.argeo.init org.argeo.enterprise