adding basic file - exploded jar - support
diff --git a/README.adoc b/README.adoc
index 0b4f225..5edea6e 100644
--- a/README.adoc
+++ b/README.adoc
@@ -80,4 +80,21 @@
</plugins>
</build>
</project>
-----
\ No newline at end of file
+----
+
+How to add a command? Create a jar with this class:
+
+[source,java]
+----
+@Service
+@Command(name = "hello", scope = "test")
+public class MyCommand implements Action {
+ public Object execute() throws Exception {
+ System.out.println("Hello world");
+ return "hello world";
+ }
+}
+----
+
+Then package it as a normal jar - not even a bundle - and add it
+in the previous classpath. You can now run "test:hello".
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 778dac1..84b0826 100644
--- a/pom.xml
+++ b/pom.xml
@@ -33,7 +33,7 @@
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
- <artifactId>xbean-finder</artifactId>
+ <artifactId>xbean-finder-shaded</artifactId>
<version>4.11</version>
</dependency>
<dependency>
diff --git a/src/main/java/org/apache/karaf/framework/ContextualFramework.java b/src/main/java/org/apache/karaf/framework/ContextualFramework.java
index 539b1db..317c43d 100644
--- a/src/main/java/org/apache/karaf/framework/ContextualFramework.java
+++ b/src/main/java/org/apache/karaf/framework/ContextualFramework.java
@@ -15,6 +15,7 @@
import static java.util.Arrays.asList;
import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.toList;
import java.io.File;
import java.io.IOException;
@@ -28,12 +29,17 @@
import java.time.ZoneId;
import java.util.Collection;
import java.util.Map;
+import java.util.ServiceLoader;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.function.Predicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import org.apache.karaf.framework.deployer.OSGiBundleLifecycle;
import org.apache.karaf.framework.scanner.StandaloneScanner;
+import org.apache.karaf.framework.scanner.manifest.KarafCommandManifestContributor;
+import org.apache.karaf.framework.scanner.manifest.ManifestContributor;
import org.apache.karaf.framework.service.BundleRegistry;
import org.apache.karaf.framework.service.OSGiServices;
import org.slf4j.Logger;
@@ -63,6 +69,36 @@
private File workDir = new File(System.getProperty("java.io.tmpdir"), "karaf-boot_" + UUID.randomUUID().toString());
private Predicate<String> jarFilter = it -> DEFAULT_EXCLUSIONS.stream().anyMatch(it::startsWith);
+ private Collection<String> scanningIncludes;
+ private Collection<String> scanningExcludes;
+ private Collection<ManifestContributor> manifestContributors = Stream.concat(
+ Stream.of(new KarafCommandManifestContributor()), // built-in
+ StreamSupport.stream(ServiceLoader.load(ManifestContributor.class).spliterator(), false) // extensions
+ ).collect(toList());
+
+ public Collection<ManifestContributor> getManifestContributors() {
+ return manifestContributors;
+ }
+
+ public void setManifestContributors(final Collection<ManifestContributor> manifestContributors) {
+ this.manifestContributors = manifestContributors;
+ }
+
+ public Collection<String> getScanningIncludes() {
+ return scanningIncludes;
+ }
+
+ public void setScanningIncludes(final Collection<String> scanningIncludes) {
+ this.scanningIncludes = scanningIncludes;
+ }
+
+ public Collection<String> getScanningExcludes() {
+ return scanningExcludes;
+ }
+
+ public void setScanningExcludes(final Collection<String> scanningExcludes) {
+ this.scanningExcludes = scanningExcludes;
+ }
public File getWorkDir() {
return workDir;
@@ -107,9 +143,8 @@
startTime = System.currentTimeMillis();
LOGGER.info("Starting Apache Karaf Contextual Framework on {}",
LocalDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneId.systemDefault()));
- new StandaloneScanner(configuration.getJarFilter())
- .findOSGiBundles()
- .stream()
+ final StandaloneScanner scanner = new StandaloneScanner(configuration, registry.getFramework());
+ Stream.concat(scanner.findOSGiBundles().stream(), scanner.findPotentialOSGiBundles().stream())
.sorted(comparing(b -> b.getJar().getName()))
.map(it -> new OSGiBundleLifecycle(it.getManifest(), it.getJar(), services, registry, configuration))
.peek(OSGiBundleLifecycle::start)
diff --git a/src/main/java/org/apache/karaf/framework/deployer/BundleImpl.java b/src/main/java/org/apache/karaf/framework/deployer/BundleImpl.java
index 63f8c76..a47bc3e 100644
--- a/src/main/java/org/apache/karaf/framework/deployer/BundleImpl.java
+++ b/src/main/java/org/apache/karaf/framework/deployer/BundleImpl.java
@@ -23,7 +23,14 @@
import java.io.InputStream;
import java.net.MalformedURLException;
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.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
@@ -203,6 +210,22 @@
@Override
public Enumeration<String> getEntryPaths(final String path) {
+ if (file.isDirectory()) {
+ final Path base = file.toPath();
+ final Collection<String> paths = new ArrayList<>();
+ try {
+ Files.walkFileTree(base, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ paths.add(base.relativize(file).toString());
+ return super.visitFile(file, attrs);
+ }
+ });
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ return enumeration(paths);
+ }
try (final JarFile jar = new JarFile(file)) {
return enumeration(list(jar.entries()).stream()
.filter(it -> it.getName().startsWith(path))
@@ -228,35 +251,65 @@
final Filter filter = filePattern == null ?
null : context.createFilter("(filename=" + filePattern + ")");
final String prefix = path == null ? "" : (path.startsWith("/") ? path.substring(1) : path);
- try (final JarFile jar = new JarFile(file)) { // todo: suport exploded folders
- return enumeration(list(jar.entries()).stream()
- .filter(it -> it.getName().startsWith(prefix))
- .map(ZipEntry::getName)
- .filter(name -> !name.endsWith("/")) // folders
- .filter(name -> { // todo: enrich
- if (filter == null) {
- return true;
- }
- if (name.equals(prefix + '/' + filePattern)) {
- return true;
- }
- final Hashtable<String, Object> props = new Hashtable<>();
- props.put("filename", name);
- return filter.matches(props);
- })
- .map(name -> {
- try {
- return new URL("jar", null, file.toURI().toURL().toExternalForm() + "!/" + name);
- } catch (final MalformedURLException e) {
- throw new IllegalArgumentException(e);
- }
- })
- .collect(toList()));
- } catch (final IOException e) {
- throw new IllegalArgumentException(e);
+ final File baseFile = new File(file, prefix);
+ final Path base = baseFile.toPath();
+ if (baseFile.isDirectory()) {
+ if (!recurse) {
+ return enumeration(ofNullable(baseFile.listFiles())
+ .map(Stream::of)
+ .orElseGet(Stream::empty)
+ .filter(file -> doFilterEntry(filter, base.relativize(file.toPath()).toString()))
+ .map(f -> {
+ try {
+ return f.getAbsoluteFile().toURI().toURL();
+ } catch (final MalformedURLException e) {
+ throw new IllegalStateException(e);
+ }
+ })
+ .collect(toList()));
+ } else {
+ final Collection<URL> files = new ArrayList<>();
+ try {
+ Files.walkFileTree(base, new SimpleFileVisitor<Path>() {
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ if (doFilterEntry(filter, base.relativize(file).toString())) {
+ files.add(file.toAbsolutePath().toUri().toURL());
+ }
+ return super.visitFile(file, attrs);
+ }
+ });
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ return enumeration(files);
+ }
+ } else {
+ try (final JarFile jar = new JarFile(file)) {
+ return enumeration(list(jar.entries()).stream().filter(it -> it.getName().startsWith(prefix))
+ .map(ZipEntry::getName).filter(name -> !name.endsWith("/")) // folders
+ .filter(name -> doFilterEntry(filter, name)).map(name -> {
+ try {
+ return new URL("jar", null, file.toURI().toURL().toExternalForm() + "!/" + name);
+ } catch (final MalformedURLException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }).collect(toList()));
+ } catch (final IOException e) {
+ throw new IllegalArgumentException(e);
+ }
}
}
+ private boolean doFilterEntry(final Filter filter, final String name) {
+ if (filter == null) {
+ return true;
+ }
+ final Hashtable<String, Object> props = new Hashtable<>();
+ props.put("filename", name);
+ return filter.matches(props);
+ }
+
@Override
public BundleContext getBundleContext() {
return context;
diff --git a/src/main/java/org/apache/karaf/framework/scanner/KnownJarsFilter.java b/src/main/java/org/apache/karaf/framework/scanner/KnownJarsFilter.java
new file mode 100644
index 0000000..c998cad
--- /dev/null
+++ b/src/main/java/org/apache/karaf/framework/scanner/KnownJarsFilter.java
@@ -0,0 +1,230 @@
+/**
+ * Copyright (C) 2006-2018 Talend Inc. - www.talend.com
+ * <p>
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.karaf.framework.scanner;
+
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toSet;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.function.Predicate;
+
+import org.apache.karaf.framework.ContextualFramework;
+
+public class KnownJarsFilter implements Predicate<String> {
+ private final Collection<String> forceIncludes = new HashSet<>(); // none for now
+ private final Collection<String> excludes = new HashSet<String>() {{
+ add("activation-");
+ add("activeio-");
+ add("activemq-");
+ add("aether-");
+ add("akka-");
+ add("ant-");
+ add("antlr-");
+ add("aopalliance-");
+ add("ApacheJMeter");
+ add("apiguardian-");
+ add("args4j-");
+ add("arquillian-");
+ add("asciidoctor");
+ add("asm-");
+ add("async-http-client-");
+ add("avalon-framework-");
+ add("axis");
+ add("batchee");
+ add("batik-");
+ add("bcprov-");
+ add("bootstrap");
+ add("bsf-");
+ add("bval");
+ add("c3p0-");
+ add("cassandra-driver-core");
+ add("catalina");
+ add("cglib-");
+ add("charsets.jar");
+ add("commons");
+ add("cryptacular-");
+ add("cssparser-");
+ add("cxf-");
+ add("deploy");
+ add("derby");
+ add("dom4j");
+ add("ecj-");
+ add("eclipselink-");
+ add("ehcache-");
+ add("el-api");
+ add("FastInfoset");
+ add("freeemarker-");
+ add("fusemq-leveldb-");
+ add("geronimo-");
+ add("google-");
+ add("gpars-");
+ add("gragent.jar");
+ add("groovy-");
+ add("gson-");
+ add("guava-");
+ add("guice-");
+ add("h2-");
+ add("hamcrest-");
+ add("hawt");
+ add("hibernate-");
+ add("howl-");
+ add("hsqldb-");
+ add("htmlunit-");
+ add("httpclient-");
+ add("httpcore-");
+ add("icu4j-");
+ add("idb-");
+ add("idea_rt.jar");
+ add("istack-commons-runtime-");
+ add("ivy-");
+ add("jackson-");
+ add("janino-");
+ add("jansi-");
+ add("jasper");
+ add("jasypt-");
+ add("java");
+ add("jaxb-");
+ add("jaxp-");
+ add("jbake-");
+ add("jboss");
+ add("jce.jar");
+ add("jcommander-");
+ add("jersey-");
+ add("jettison-");
+ add("jetty-");
+ add("jfr.jar");
+ add("jfxrt.jar");
+ add("jline");
+ add("jmdns-");
+ add("jna-");
+ add("jnr-");
+ add("joda-time-");
+ add("johnzon-");
+ add("jolokia-");
+ add("jruby-");
+ add("json");
+ add("jsoup-");
+ add("jsp");
+ add("jsr");
+ add("jsse.jar");
+ add("jul");
+ add("junit");
+ add("jython-");
+ add("kahadb-");
+ add("kotlin-runtime");
+ add("leveldb");
+ add("log");
+ add("lombok-");
+ add("lucene");
+ add("management-agent.jar");
+ add("maven-");
+ add("mbean-annotation-api-");
+ add("meecrowave-");
+ add("microprofile-");
+ add("mimepull-");
+ add("mina-");
+ add("mqtt-client-");
+ add("multiverse-core-");
+ add("myfaces-");
+ add("mysql-");
+ add("neethi-");
+ add("nekohtml-");
+ add("netty-");
+ add("openjpa-");
+ add("openmdx-");
+ add("opensaml-");
+ add("opentest4j-");
+ add("openwebbeans-");
+ add("openws-");
+ add("ops4j-");
+ add("org.apache.aries");
+ add("org.eclipse.");
+ add("org.jacoco.agent");
+ add("org.junit.");
+ add("org.osgi.");
+ add("orient-");
+ add("oro-");
+ add("pax");
+ add("PDFBox");
+ add("plexus-");
+ add("plugin.jar");
+ add("poi-");
+ add("qdox-");
+ add("quartz");
+ add("resources.jar");
+ add("rhino-");
+ add("rmock-");
+ add("rt.jar");
+ add("saaj-");
+ add("sac-");
+ add("scala");
+ add("scannotation-");
+ add("serializer-");
+ add("serp-");
+ add("servlet-api-");
+ add("shrinkwrap-");
+ add("sisu-guice");
+ add("sisu-inject");
+ add("slf4j-");
+ add("smack");
+ add("snappy-");
+ add("spring-");
+ add("sshd-");
+ add("stax");
+ add("sunec.jar");
+ add("surefire-");
+ add("swizzle-");
+ add("sxc-");
+ add("testng-");
+ add("tomcat");
+ add("tomee-");
+ add("tools.jar");
+ add("twitter4j-");
+ add("validation-api-");
+ add("velocity-");
+ add("wagon-");
+ add("webbeans");
+ add("websocket");
+ add("woodstox-core-");
+ add("ws-commons-util-");
+ add("wsdl4j-");
+ add("wss4j-");
+ add("wstx-asl-");
+ add("xalan-");
+ add("xbean-");
+ add("xercesImpl-");
+ add("xml");
+ add("XmlSchema-");
+ add("xstream-");
+ add("zipfs.jar");
+ add("ziplock-");
+ }};
+
+ public KnownJarsFilter(final ContextualFramework.Configuration config) {
+ ofNullable(config.getScanningIncludes()).ifPresent(i -> {
+ forceIncludes.clear();
+ forceIncludes.addAll(i.stream().map(String::trim).filter(j -> !j.isEmpty()).collect(toSet()));
+ });
+ ofNullable(config.getScanningExcludes())
+ .ifPresent(i -> excludes.addAll(i.stream().map(String::trim).filter(j -> !j.isEmpty()).collect(toSet())));
+ }
+
+ @Override
+ public boolean test(final String jarName) {
+ return forceIncludes.stream().anyMatch(jarName::startsWith) || excludes.stream().noneMatch(jarName::startsWith);
+ }
+}
diff --git a/src/main/java/org/apache/karaf/framework/scanner/StandaloneScanner.java b/src/main/java/org/apache/karaf/framework/scanner/StandaloneScanner.java
index 272595c..9175da8 100644
--- a/src/main/java/org/apache/karaf/framework/scanner/StandaloneScanner.java
+++ b/src/main/java/org/apache/karaf/framework/scanner/StandaloneScanner.java
@@ -14,57 +14,119 @@
package org.apache.karaf.framework.scanner;
import static java.util.stream.Collectors.toList;
+import static org.apache.xbean.finder.archive.ClasspathArchive.archive;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
import java.util.Collection;
+import java.util.List;
import java.util.Objects;
-import java.util.function.Predicate;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
+import org.apache.karaf.framework.ContextualFramework;
+import org.apache.karaf.framework.scanner.manifest.ManifestCreator;
+import org.apache.xbean.finder.AnnotationFinder;
import org.apache.xbean.finder.ClassLoaders;
import org.apache.xbean.finder.UrlSet;
+import org.apache.xbean.finder.archive.Archive;
import org.apache.xbean.finder.util.Files;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class StandaloneScanner {
-
+ private final static Logger LOGGER = LoggerFactory.getLogger(StandaloneScanner.class);
private static final Attributes.Name OSGI_MANIFEST_MARKER = new Attributes.Name("Bundle-Version");
- private final Predicate<String> filter;
+ private final List<URL> urls;
+ private final ContextualFramework.Configuration configuration;
+ private final ClassLoader loader;
+ private final File frameworkJar;
- public StandaloneScanner(final Predicate<String> jarFilter) {
- this.filter = jarFilter;
- }
-
- public Collection<BundleDefinition> findOSGiBundles() {
+ public StandaloneScanner(final ContextualFramework.Configuration configuration, final File frameworkJar) {
+ this.configuration = configuration;
+ this.frameworkJar = frameworkJar;
+ this.loader = Thread.currentThread().getContextClassLoader();
try {
- return new UrlSet(ClassLoaders.findUrls(Thread.currentThread().getContextClassLoader()))
+ this.urls = new UrlSet(ClassLoaders.findUrls(loader))
.excludeJvm()
- .getUrls()
- .stream()
- .map(Files::toFile)
- .filter(this::isIncluded)
- .map(this::toDefinition)
- .filter(Objects::nonNull)
- .collect(toList());
+ .getUrls();
} catch (final IOException e) {
throw new IllegalStateException(e);
}
}
+ public Collection<BundleDefinition> findPotentialOSGiBundles() {
+ final KnownJarsFilter filter = new KnownJarsFilter(configuration);
+ return urls.stream()
+ .map(it -> new FileAndUrl(Files.toFile(it), it))
+ .filter(it -> !it.file.getAbsoluteFile().equals(frameworkJar))
+ .filter(it -> filter.test(it.file.getName()))
+ .filter(it -> toDefinition(it.file) == null)
+ .map(it -> {
+ final Archive jarArchive = archive(loader, it.url);
+ // we scan per archive to be able to create bundle after
+ try {
+ final AnnotationFinder archiveFinder = new AnnotationFinder(jarArchive);
+ final ManifestCreator manifestCreator = new ManifestCreator(it.file.getName());
+ configuration.getManifestContributors()
+ .forEach(c -> c.contribute(archiveFinder, manifestCreator));
+ final Manifest manifest = manifestCreator.getManifest();
+ if (manifest == null) {
+ LOGGER.debug("{} was scanned for nothing, maybe adjust scanning exclusions", it.file);
+ return null;
+ }
+ LOGGER.debug("{} was scanned and is converted to a bundle", it.file);
+ return new BundleDefinition(manifest, it.file);
+ } catch (final LinkageError e) {
+ LOGGER.debug("{} is not scannable, maybe exclude it in framework configuration", it.file);
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(toList());
+ }
+
+ public Collection<BundleDefinition> findOSGiBundles() {
+ return urls
+ .stream()
+ .map(Files::toFile)
+ .filter(this::isIncluded)
+ .map(this::toDefinition)
+ .filter(Objects::nonNull)
+ .collect(toList());
+ }
+
private boolean isIncluded(final File file) {
- return !filter.test(file.getName());
+ return !configuration.getJarFilter().test(file.getName());
}
private BundleDefinition toDefinition(final File file) {
+ if (file.isDirectory()) {
+ final File manifest = new File(file, "META-INF/MANIFEST.MF");
+ if (manifest.exists()) {
+ try (final InputStream stream = new FileInputStream(manifest)) {
+ final Manifest mf = new Manifest(stream);
+ if (isOSGi(mf)) {
+ return new BundleDefinition(mf, file);
+ }
+ return null;
+ } catch (final IOException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ return null;
+ }
try (final JarFile jar = new JarFile(file)) {
final Manifest manifest = jar.getManifest();
if (manifest == null) {
return null;
}
- if (manifest.getMainAttributes().containsKey(OSGI_MANIFEST_MARKER)) {
+ if (isOSGi(manifest)) {
return new BundleDefinition(manifest, file);
}
return null;
@@ -73,6 +135,10 @@
}
}
+ private boolean isOSGi(final Manifest mf) {
+ return mf.getMainAttributes().containsKey(OSGI_MANIFEST_MARKER);
+ }
+
public static class BundleDefinition {
private final Manifest manifest;
private final File jar;
@@ -90,4 +156,14 @@
return jar;
}
}
+
+ private static class FileAndUrl {
+ private final File file;
+ private final URL url;
+
+ private FileAndUrl(final File file, final URL url) {
+ this.file = file;
+ this.url = url;
+ }
+ }
}
diff --git a/src/main/java/org/apache/karaf/framework/scanner/manifest/KarafCommandManifestContributor.java b/src/main/java/org/apache/karaf/framework/scanner/manifest/KarafCommandManifestContributor.java
new file mode 100644
index 0000000..3f44719
--- /dev/null
+++ b/src/main/java/org/apache/karaf/framework/scanner/manifest/KarafCommandManifestContributor.java
@@ -0,0 +1,45 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.karaf.framework.scanner.manifest;
+
+import static java.util.stream.Collectors.joining;
+
+import java.lang.annotation.Annotation;
+import java.util.Objects;
+import java.util.function.Supplier;
+import java.util.jar.Manifest;
+
+import org.apache.xbean.finder.AnnotationFinder;
+
+public class KarafCommandManifestContributor implements ManifestContributor {
+ @Override
+ public void contribute(final AnnotationFinder finder, final Supplier<Manifest> manifest) {
+ try {
+ final Class<? extends Annotation> commandMarker = (Class<? extends Annotation>)
+ finder.getArchive().loadClass("org.apache.karaf.shell.api.action.lifecycle.Service");
+ final String packages = finder.findAnnotatedClasses(commandMarker)
+ .stream()
+ .map(Class::getPackage)
+ .filter(Objects::nonNull)
+ .map(Package::getName)
+ .distinct()
+ .collect(joining(","));
+ if (!packages.isEmpty()) {
+ manifest.get().getMainAttributes().putValue("Karaf-Commands", packages);
+ }
+ } catch (final ClassNotFoundException e) {
+ // no-op
+ }
+ }
+}
diff --git a/src/main/java/org/apache/karaf/framework/scanner/manifest/ManifestContributor.java b/src/main/java/org/apache/karaf/framework/scanner/manifest/ManifestContributor.java
new file mode 100644
index 0000000..6f12867
--- /dev/null
+++ b/src/main/java/org/apache/karaf/framework/scanner/manifest/ManifestContributor.java
@@ -0,0 +1,23 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.karaf.framework.scanner.manifest;
+
+import java.util.function.Supplier;
+import java.util.jar.Manifest;
+
+import org.apache.xbean.finder.AnnotationFinder;
+
+public interface ManifestContributor {
+ void contribute(final AnnotationFinder finder, final Supplier<Manifest> manifest);
+}
diff --git a/src/main/java/org/apache/karaf/framework/scanner/manifest/ManifestCreator.java b/src/main/java/org/apache/karaf/framework/scanner/manifest/ManifestCreator.java
new file mode 100644
index 0000000..3c85048
--- /dev/null
+++ b/src/main/java/org/apache/karaf/framework/scanner/manifest/ManifestCreator.java
@@ -0,0 +1,42 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.karaf.framework.scanner.manifest;
+
+import java.util.function.Supplier;
+import java.util.jar.Manifest;
+
+public class ManifestCreator implements Supplier<Manifest> {
+ private Manifest manifest;
+ private final String name;
+
+ public ManifestCreator(final String name) {
+ this.name = name;
+ }
+
+ @Override
+ public Manifest get() {
+ return manifest == null ? manifest = create() : manifest;
+ }
+
+ public Manifest getManifest() {
+ return manifest;
+ }
+
+ private Manifest create() {
+ final Manifest manifest = new Manifest();
+ manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
+ manifest.getMainAttributes().putValue("Bundle-SymbolicName", name);
+ return manifest;
+ }
+}
diff --git a/src/main/java/org/apache/karaf/framework/service/BundleRegistry.java b/src/main/java/org/apache/karaf/framework/service/BundleRegistry.java
index ab165fd..95a4d5d 100644
--- a/src/main/java/org/apache/karaf/framework/service/BundleRegistry.java
+++ b/src/main/java/org/apache/karaf/framework/service/BundleRegistry.java
@@ -25,19 +25,22 @@
public class BundleRegistry {
private final Map<Long, OSGiBundleLifecycle> bundles = new HashMap<>();
+ private final File framework;
public BundleRegistry(final OSGiServices services, final ContextualFramework.Configuration configuration) {
+ this.framework = toFile(Thread.currentThread().getContextClassLoader().getResource(getClass().getName().replace('.', '/') + ".class"))
+ .getAbsoluteFile();
+
// ensure we have the framework bundle simulated
final Manifest frameworkManifest = new Manifest();
frameworkManifest.getMainAttributes().putValue("Manifest-Version", "1.0");
frameworkManifest.getMainAttributes().putValue("Bundle-Version", "1.0");
frameworkManifest.getMainAttributes().putValue("Bundle-SymbolicName", "Contextual Framework");
- bundles.put(0L, new OSGiBundleLifecycle(frameworkManifest, getThisJar(), services, this, configuration));
+ bundles.put(0L, new OSGiBundleLifecycle(frameworkManifest, framework, services, this, configuration));
}
- private File getThisJar() {
- final String resource = getClass().getName().replace('.', '/') + ".class";
- return toFile(Thread.currentThread().getContextClassLoader().getResource(resource));
+ public File getFramework() {
+ return framework;
}
public Map<Long, OSGiBundleLifecycle> getBundles() {
diff --git a/src/main/java/org/apache/karaf/framework/service/ImplicitManifestService.java b/src/main/java/org/apache/karaf/framework/service/ImplicitManifestService.java
new file mode 100644
index 0000000..65a6d20
--- /dev/null
+++ b/src/main/java/org/apache/karaf/framework/service/ImplicitManifestService.java
@@ -0,0 +1,18 @@
+/**
+ * Copyright (C) 2006-2018 Talend Inc. - www.talend.com
+ * <p>
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.apache.karaf.framework.service;
+
+public class ImplicitManifestService {}