This is an automated email from the ASF dual-hosted git repository. davidb pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
The following commit(s) were added to refs/heads/master by this push: new 844e5939 Native compile changes for atomos feature launcher, config and weaver (#114) 844e5939 is described below commit 844e593916e0b0cfe5ad61a4bd9dccdfd34a1c69 Author: David Bosschaert <bossc...@adobe.com> AuthorDate: Mon Dec 11 09:40:29 2023 +0000 Native compile changes for atomos feature launcher, config and weaver (#114) --- atomosfeaturelauncher/Dockerfile | 15 +- atomosfeaturelauncher/build.sh | 11 +- .../{build.sh => build_nodocker.sh} | 16 +- atomosfeaturelauncher/launch.sh | 13 + .../src/main/features/feature.json | 6 + .../feature/launcher/atomos/AtomosLauncher.java | 28 +- .../feature/launcher/atomos/AtomosRunner.java | 85 +++--- atomosfeaturelauncherconfig/pom.xml | 5 + .../atomos/config/AtomosConfigLauncher.java | 320 +++++++++++++++++---- .../launcher/atomos/config/IndexPlugin.java | 29 +- .../atomos/weaver/impl/AtomosWeaverVisitor.java | 25 +- 11 files changed, 404 insertions(+), 149 deletions(-) diff --git a/atomosfeaturelauncher/Dockerfile b/atomosfeaturelauncher/Dockerfile index 445e3bbb..f26d5272 100644 --- a/atomosfeaturelauncher/Dockerfile +++ b/atomosfeaturelauncher/Dockerfile @@ -2,13 +2,14 @@ FROM ghcr.io/graalvm/native-image:ol8-java17-22.3.3 as native COPY target/artifacts artifacts COPY --chmod=777 build.sh build.sh -COPY target/atomos-config/atomos_init.sh atomos_init.sh -COPY target/atomos-config/resource-config.json resource-config.json -COPY target/atomos-config/reflect-config.json reflect-config.json -COPY target/atomos-config/proxy-config.json proxy-config.json -COPY target/atomos-config/jni-config.json jni-config.json -COPY target/atomos-config/serialization-config.json serialization-config.json -COPY target/atomos-config/atomos.substrate.jar atomos.substrate.jar +#COPY target/atomos-config/atomos_init.sh atomos_init.sh +#COPY target/atomos-config/resource-config.json resource-config.json +#COPY target/atomos-config/reflect-config.json reflect-config.json +#COPY target/atomos-config/proxy-config.json proxy-config.json +#COPY target/atomos-config/jni-config.json jni-config.json +#COPY target/atomos-config/serialization-config.json serialization-config.json +#COPY target/atomos-config/atomos.substrate.jar atomos.substrate.jar +COPY target/atomos-config/app.substrate.jar app.substrate.jar RUN ./build.sh "-H:+StaticExecutableWithDynamicLibC" "-o launcher" "-Ob" diff --git a/atomosfeaturelauncher/build.sh b/atomosfeaturelauncher/build.sh old mode 100644 new mode 100755 index 8d1cc8fd..d068e9dc --- a/atomosfeaturelauncher/build.sh +++ b/atomosfeaturelauncher/build.sh @@ -1,7 +1,5 @@ #!/bin/sh -source ./atomos_init.sh - native-image --verbose \ -cp "artifacts/org/apache/sling/org.apache.sling.feature.launcher.atomos/0.0.1-SNAPSHOT/org.apache.sling.feature.launcher.atomos-0.0.1-SNAPSHOT.jar:\ artifacts/org/apache/sling/org.apache.sling.feature.launcher/1.2.4/org.apache.sling.feature.launcher-1.2.4.jar:\ @@ -10,14 +8,9 @@ artifacts/org/slf4j/slf4j-simple/1.7.25/slf4j-simple-1.7.25.jar:\ artifacts/org/apache/sling/org.apache.sling.feature/1.3.0/org.apache.sling.feature-1.3.0.jar:\ artifacts/org/apache/felix/org.apache.felix.cm.json/1.0.6/org.apache.felix.cm.json-1.0.6.jar:\ artifacts/commons-cli/commons-cli/1.4/commons-cli-1.4.jar:\ -$ATOMOS_CLASSPATH" \ +artifacts/org/apache/felix/org.apache.felix.framework/7.0.5/org.apache.felix.framework-7.0.5.jar:\ +app.substrate.jar" \ org.apache.sling.feature.launcher.atomos.AtomosLaucherMain \ --no-fallback --enable-https --enable-http \ -"$ATOMOS_INIT" \ -"-H:ResourceConfigurationFiles=resource-config.json" \ -"-H:ReflectionConfigurationFiles=reflect-config.json" \ -"-H:DynamicProxyConfigurationFiles=proxy-config.json" \ -"-H:JNIConfigurationFiles=jni-config.json" \ -"-H:SerializationConfigurationFiles=serialization-config.json" \ "$@" diff --git a/atomosfeaturelauncher/build.sh b/atomosfeaturelauncher/build_nodocker.sh old mode 100644 new mode 100755 similarity index 67% copy from atomosfeaturelauncher/build.sh copy to atomosfeaturelauncher/build_nodocker.sh index 8d1cc8fd..b3fabc82 --- a/atomosfeaturelauncher/build.sh +++ b/atomosfeaturelauncher/build_nodocker.sh @@ -1,6 +1,8 @@ #!/bin/sh -source ./atomos_init.sh +pushd target + +source ./atomos-config/atomos_init.sh native-image --verbose \ -cp "artifacts/org/apache/sling/org.apache.sling.feature.launcher.atomos/0.0.1-SNAPSHOT/org.apache.sling.feature.launcher.atomos-0.0.1-SNAPSHOT.jar:\ @@ -14,10 +16,12 @@ $ATOMOS_CLASSPATH" \ org.apache.sling.feature.launcher.atomos.AtomosLaucherMain \ --no-fallback --enable-https --enable-http \ "$ATOMOS_INIT" \ -"-H:ResourceConfigurationFiles=resource-config.json" \ -"-H:ReflectionConfigurationFiles=reflect-config.json" \ -"-H:DynamicProxyConfigurationFiles=proxy-config.json" \ -"-H:JNIConfigurationFiles=jni-config.json" \ -"-H:SerializationConfigurationFiles=serialization-config.json" \ +"-H:ResourceConfigurationFiles=atomos-config/resource-config.json" \ +"-H:ReflectionConfigurationFiles=atomos-config/reflect-config.json" \ +"-H:DynamicProxyConfigurationFiles=atomos-config/proxy-config.json" \ +"-H:JNIConfigurationFiles=atomos-config/jni-config.json" \ +"-H:SerializationConfigurationFiles=atomos-config/serialization-config.json" \ +"-o aem_native" "-Ob" \ "$@" +popd diff --git a/atomosfeaturelauncher/launch.sh b/atomosfeaturelauncher/launch.sh new file mode 100755 index 00000000..f7c72861 --- /dev/null +++ b/atomosfeaturelauncher/launch.sh @@ -0,0 +1,13 @@ +pushd target +rm -rf launcher +java -agentlib:jdwp=transport=dt_socket,address=*:7777,server=y,suspend=n -cp "artifacts/org/apache/sling/org.apache.sling.feature.launcher.atomos/0.0.1-SNAPSHOT/org.apache.sling.feature.launcher.atomos-0.0.1-SNAPSHOT.jar:\ +artifacts/org/apache/sling/org.apache.sling.feature.launcher/1.2.4/org.apache.sling.feature.launcher-1.2.4.jar:\ +artifacts/org/apache/felix/org.apache.felix.atomos/1.0.1-SNAPSHOT/org.apache.felix.atomos-1.0.1-SNAPSHOT.jar:\ +artifacts/org/slf4j/slf4j-simple/1.7.25/slf4j-simple-1.7.25.jar:\ +artifacts/org/apache/sling/org.apache.sling.feature/1.3.0/org.apache.sling.feature-1.3.0.jar:\ +artifacts/org/apache/felix/org.apache.felix.cm.json/1.0.6/org.apache.felix.cm.json-1.0.6.jar:\ +artifacts/commons-cli/commons-cli/1.4/commons-cli-1.4.jar:\ +artifacts/org/apache/felix/org.apache.felix.framework/7.0.5/org.apache.felix.framework-7.0.5.jar:\ +atomos-config/app.substrate.jar" \ +org.apache.sling.feature.launcher.atomos.AtomosLaucherMain +popd diff --git a/atomosfeaturelauncher/src/main/features/feature.json b/atomosfeaturelauncher/src/main/features/feature.json index d650cbf8..59b79c97 100644 --- a/atomosfeaturelauncher/src/main/features/feature.json +++ b/atomosfeaturelauncher/src/main/features/feature.json @@ -565,6 +565,12 @@ { "name":"org.owasp.esapi.reference.JavaLogFactory", "methods":[{"name":"getInstance","parameterTypes":[] }] + }, + { + "name":"org.apache.felix.webconsole.SimpleWebConsolePlugin", + "queryAllDeclaredMethods":true, + "queryAllPublicMethods":true, + "methods":[{"name":"getResource","parameterTypes":["java.lang.String"] }] } ], "resource-config": { diff --git a/atomosfeaturelauncher/src/main/java/org/apache/sling/feature/launcher/atomos/AtomosLauncher.java b/atomosfeaturelauncher/src/main/java/org/apache/sling/feature/launcher/atomos/AtomosLauncher.java index 4e6972ef..a5e6d7b1 100644 --- a/atomosfeaturelauncher/src/main/java/org/apache/sling/feature/launcher/atomos/AtomosLauncher.java +++ b/atomosfeaturelauncher/src/main/java/org/apache/sling/feature/launcher/atomos/AtomosLauncher.java @@ -18,35 +18,15 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package org.apache.sling.feature.launcher.atomos; -import org.apache.felix.atomos.Atomos; -import org.apache.felix.atomos.AtomosContent; -import org.apache.felix.atomos.AtomosLayer; +import java.io.IOException; +import java.net.URL; + import org.apache.sling.feature.ArtifactId; import org.apache.sling.feature.Feature; -import org.apache.sling.feature.launcher.atomos.weaver.AtomosWeaver; import org.apache.sling.feature.launcher.impl.launchers.FrameworkLauncher; import org.apache.sling.feature.launcher.spi.LauncherPrepareContext; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleException; -import org.osgi.framework.Version; -import org.osgi.framework.connect.ConnectContent; import org.slf4j.Logger; -import java.io.File; -import java.io.IOException; -import java.net.JarURLConnection; -import java.net.URL; -import java.net.URLClassLoader; -import java.security.CodeSource; -import java.security.ProtectionDomain; -import java.security.cert.Certificate; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.Optional; -import java.util.ServiceLoader; -import java.util.concurrent.ConcurrentHashMap; -import java.util.jar.JarFile; - public class AtomosLauncher extends FrameworkLauncher { @Override public void prepare(LauncherPrepareContext launcherPrepareContext, ArtifactId artifactId, Feature feature) throws Exception { @@ -73,6 +53,7 @@ public class AtomosLauncher extends FrameworkLauncher { return AtomosRunner.class.getName(); } + /* @Override public LauncherClassLoader createClassLoader() { return LOADER; @@ -145,4 +126,5 @@ public class AtomosLauncher extends FrameworkLauncher { return parent.getResources(name); } } + */ } diff --git a/atomosfeaturelauncher/src/main/java/org/apache/sling/feature/launcher/atomos/AtomosRunner.java b/atomosfeaturelauncher/src/main/java/org/apache/sling/feature/launcher/atomos/AtomosRunner.java index 7edf1530..6ac477ef 100644 --- a/atomosfeaturelauncher/src/main/java/org/apache/sling/feature/launcher/atomos/AtomosRunner.java +++ b/atomosfeaturelauncher/src/main/java/org/apache/sling/feature/launcher/atomos/AtomosRunner.java @@ -18,32 +18,9 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package org.apache.sling.feature.launcher.atomos; -import org.apache.felix.atomos.Atomos; -import org.apache.felix.atomos.AtomosContent; -import org.apache.felix.atomos.AtomosLayer; -import org.apache.felix.atomos.impl.base.AtomosBase; -import org.apache.felix.framework.BundleWiringImpl; -import org.apache.sling.feature.ArtifactId; -import org.apache.sling.feature.io.IOUtils; -import org.apache.sling.feature.launcher.impl.launchers.FrameworkRunner; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleException; -import org.osgi.framework.BundleReference; -import org.osgi.framework.Constants; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.Version; -import org.osgi.framework.connect.ConnectContent; -import org.osgi.framework.connect.FrameworkUtilHelper; -import org.osgi.framework.launch.Framework; -import org.osgi.framework.launch.FrameworkFactory; -import org.osgi.framework.startlevel.BundleStartLevel; - -import javax.swing.text.html.Option; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; @@ -58,11 +35,26 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; +import org.apache.felix.atomos.Atomos; +import org.apache.felix.atomos.AtomosContent; +import org.apache.felix.atomos.impl.base.AtomosBase; +import org.apache.sling.feature.launcher.impl.launchers.FrameworkRunner; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleException; +import org.osgi.framework.BundleReference; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.connect.ConnectContent; +import org.osgi.framework.launch.Framework; +import org.osgi.framework.launch.FrameworkFactory; +import org.osgi.framework.startlevel.BundleStartLevel; + public class AtomosRunner extends FrameworkRunner { private static final ConcurrentHashMap<String, ClassLoader> location2Loader = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<String, Bundle> classToBundle = new ConcurrentHashMap<>(); - private static final Atomos m_atomos = Atomos.newAtomos(); + private static Atomos m_atomos; private BiConsumer<URL, Map<String, String>> bundleReporter; public AtomosRunner(Map<String, String> frameworkProperties, Map<Integer, List<URL>> bundlesMap, List<Object[]> configurations, List<URL> installables) throws Exception { @@ -79,6 +71,13 @@ public class AtomosRunner extends FrameworkRunner { return result; } + private synchronized static Atomos getAtomos() { + if (m_atomos == null) { + m_atomos = Atomos.newAtomos(); + } + return m_atomos; + } + private static int getProperty(BundleContext bc, String propName, int defaultValue) { String val = bc.getProperty(propName); if (val == null) { @@ -90,7 +89,7 @@ public class AtomosRunner extends FrameworkRunner { @Override protected FrameworkFactory getFrameworkFactory() throws Exception { - return new AtomosFrameworkFactory(m_atomos); + return new AtomosFrameworkFactory(getAtomos()); } @Override @@ -121,16 +120,12 @@ public class AtomosRunner extends FrameworkRunner { private void install(final Framework framework, final Map<Integer, List<URL>> bundleMap) throws BundleException { System.out.println(System.getProperty("java.specification.version")); final BundleContext bc = framework.getBundleContext(); - System.out.println(bc.getBundle(0).getHeaders()); + // System.out.println(bc.getBundle(0).getHeaders()); int defaultStartLevel = getProperty(bc, "felix.startlevel.bundle", 1); System.out.println(new File(".").getAbsolutePath()); System.out.println(new File(new File("."), "content").getAbsolutePath()); - /*for (File child : new File(new File("."), "content").listFiles()) { - System.out.println(child.getAbsolutePath()); - }*/ - for (final Integer startLevel : sortStartLevels(bundleMap.keySet(), defaultStartLevel)) { logger.debug("Installing bundles with start level {}", startLevel); @@ -144,7 +139,7 @@ public class AtomosRunner extends FrameworkRunner { } }; - AtomosContent content = m_atomos + AtomosContent content = getAtomos() .getBootLayer() .getAtomosContents().stream() .filter(atomosContent -> { @@ -215,11 +210,19 @@ public class AtomosRunner extends FrameworkRunner { } public static InputStream getAtomosLoaderStreamWrapped(Class origin, String resource) { - return getAtomosLoaderWrapped(origin).getResourceAsStream(resolveName(origin, resource)); + URL u = getAtomosLoaderWrapped(origin).getResource(resolveName(origin, resource)); + if (u == null) { + return null; + } + + try { + return u.openStream(); + } catch (IOException e) { + return null; + } } public static ClassLoader getAtomosLoaderWrapped(Class origin) { - System.out.println("TRAP: " + origin.getName()); if (origin.isInterface()) { return origin.getClassLoader(); } @@ -229,7 +232,7 @@ public class AtomosRunner extends FrameworkRunner { } catch (Throwable ex) { return null; }}); - System.out.println(bundle); + return Optional.ofNullable(bundle).map(b -> location2Loader.computeIfAbsent(b.getLocation(), location -> new BundleClassLoader(origin, bundle) ) ).orElseGet(origin::getClassLoader); @@ -264,6 +267,20 @@ public class AtomosRunner extends FrameworkRunner { return result != null ? result : Collections.emptyEnumeration(); } + @Override + public InputStream getResourceAsStream(String name) { + URL u = getResource(name); + if (u == null) { + return null; + } + + try { + return u.openStream(); + } catch (IOException e) { + return null; + } + } + @Override public Bundle getBundle() { return bundle; diff --git a/atomosfeaturelauncherconfig/pom.xml b/atomosfeaturelauncherconfig/pom.xml index 7da4a115..832705fc 100644 --- a/atomosfeaturelauncherconfig/pom.xml +++ b/atomosfeaturelauncherconfig/pom.xml @@ -107,6 +107,11 @@ <artifactId>org.apache.felix.atomos.utils.core</artifactId> <version>0.9.1-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.feature.launcher.atomos.weaver</artifactId> + <version>0.0.1-SNAPSHOT</version> + </dependency> </dependencies> </project> diff --git a/atomosfeaturelauncherconfig/src/main/java/org/apache/sling/feature/launcher/atomos/config/AtomosConfigLauncher.java b/atomosfeaturelauncherconfig/src/main/java/org/apache/sling/feature/launcher/atomos/config/AtomosConfigLauncher.java index ce45e95e..45f90230 100644 --- a/atomosfeaturelauncherconfig/src/main/java/org/apache/sling/feature/launcher/atomos/config/AtomosConfigLauncher.java +++ b/atomosfeaturelauncherconfig/src/main/java/org/apache/sling/feature/launcher/atomos/config/AtomosConfigLauncher.java @@ -18,6 +18,38 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package org.apache.sling.feature.launcher.atomos.config; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipException; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonString; +import javax.script.ScriptEngineManager; + import org.apache.felix.atomos.utils.api.plugin.SubstratePlugin; import org.apache.felix.atomos.utils.core.LauncherBuilderImpl; import org.apache.felix.atomos.utils.substrate.api.resource.ResourceConfiguration; @@ -28,14 +60,14 @@ import org.apache.felix.scr.impl.logger.BundleLogger; import org.apache.sling.feature.Artifact; import org.apache.sling.feature.ArtifactId; import org.apache.sling.feature.Extension; -import org.apache.sling.feature.ExtensionState; -import org.apache.sling.feature.ExtensionType; import org.apache.sling.feature.Feature; import org.apache.sling.feature.builder.BuilderContext; import org.apache.sling.feature.builder.FeatureBuilder; import org.apache.sling.feature.builder.FeatureProvider; import org.apache.sling.feature.io.IOUtils; import org.apache.sling.feature.io.json.FeatureJSONReader; +import org.apache.sling.feature.io.json.FeatureJSONWriter; +import org.apache.sling.feature.launcher.atomos.weaver.AtomosWeaver; import org.apache.sling.feature.launcher.impl.launchers.FrameworkLauncher; import org.apache.sling.feature.launcher.spi.LauncherPrepareContext; import org.apache.sling.feature.launcher.spi.LauncherRunContext; @@ -44,27 +76,26 @@ import org.osgi.util.converter.Converter; import org.osgi.util.function.Function; import org.slf4j.LoggerFactory; -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonString; -import javax.json.JsonStructure; -import javax.script.ScriptEngineManager; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.OutputStream; -import java.io.Reader; -import java.io.StringReader; -import java.nio.charset.StandardCharsets; -import java.nio.file.Paths; -import java.util.stream.Collectors; -import java.util.stream.Stream; - public class AtomosConfigLauncher extends FrameworkLauncher { private volatile Feature m_app; + private final AtomosWeaver m_weaver; + + public AtomosConfigLauncher() { + m_weaver = getWeaver(); + + if (m_weaver == null) { + throw new IllegalStateException("AtomosWeaver not found via ServiceLoader"); + } + } + + private static AtomosWeaver getWeaver() { + Iterator<AtomosWeaver> loader = ServiceLoader.load(AtomosWeaver.class).iterator(); + if (loader.hasNext()) { + return loader.next(); + } + return null; + } @Override public void prepare(final LauncherPrepareContext context, @@ -108,53 +139,240 @@ public class AtomosConfigLauncher extends FrameworkLauncher { m_app = app; } + private JarOutputStream getJarToBuild(File inJar, File outJar) throws IOException { + try(JarFile srcJar = new JarFile(inJar)) { + JarOutputStream destJar = new JarOutputStream(new FileOutputStream(outJar), srcJar.getManifest()); + + for(JarEntry entry : Collections.list(srcJar.entries())) { + if ("META-INF/MANIFEST.MF".equals(entry.getName())) { + continue; + } + + destJar.putNextEntry(entry); + InputStream is = srcJar.getInputStream(entry); + is.transferTo(destJar); + destJar.closeEntry(); + } + + return destJar; + } + } + + private void addClassesToJar(String jarFileName, JarOutputStream outJar, ClassLoader cl) throws IOException { + try (JarFile jf = new JarFile(jarFileName)) { + for (JarEntry entry : Collections.list(jf.entries())) { + String fileName = entry.getName(); + + // only add class entries. + if (!fileName.endsWith(".class") && + !fileName.endsWith(".properties")) { // The .properties files are included here until we have weave ResourceBundle invocations + continue; + } + + if (fileName.endsWith("module-info.class")) { + continue; + } + + try { + JarEntry newEntry = new JarEntry(entry.getName()); + outJar.putNextEntry(newEntry); + InputStream is = jf.getInputStream(entry); + + if (fileName.endsWith(".class")) { + // Weave the class bytes and then write them to the target jar + String className = fileName.substring(0, fileName.length() - 6).replace('/', '.'); + byte[] classBytes = is.readAllBytes(); + + try { + byte[] woven = m_weaver.weave(classBytes, "org.apache.sling.feature.launcher.atomos.AtomosRunner", + "getAtomosLoaderWrapped", "getAtomosLoaderResourceWrapped", "getAtomosLoaderStreamWrapped", cl); + outJar.write(woven); + } catch (Exception ex) { + System.out.println("\nProblem weaving " + className + " " + ex.getMessage()); + // Use unwoven bytes + outJar.write(classBytes); + } + } else { + // It's not a class file, don't try to weave + is.transferTo(outJar); + } + outJar.closeEntry(); + } catch (ZipException ze) { + // Happens in case of a duplicate class file. + System.out.println("Warn: " + ze.getMessage()); + + continue; + } + } + } + } + + private void addNativeImageProperties(JarOutputStream jarToBuild, String iabt) throws IOException { + String iart = "--initialize-at-run-time=" + + "org.owasp.esapi.reference.DefaultValidator," + + "org.owasp.esapi.reference.JavaLogFactory$JavaLogger"; + + iabt += ",org.apache.sling.feature.launcher.atomos.AtomosRunner"; + + JarEntry je = new JarEntry("META-INF/native-image/app/native-image.properties"); + jarToBuild.putNextEntry(je); + String args = "Args = " + iabt + " " + iart + System.lineSeparator(); + jarToBuild.write(args.getBytes()); + jarToBuild.closeEntry(); + } + + private List<String> extractBundleClassPathJars(List<String> jars, File targetDir) throws IOException { + targetDir.mkdirs(); + List<String> result = new ArrayList<>(); + + for (String jar : jars) { + File file = new File(jar); + try (JarFile jf = new JarFile(file)) { + Manifest mf = jf.getManifest(); + Attributes ma = mf.getMainAttributes(); + String bcp = ma.getValue("Bundle-ClassPath"); + + if (bcp == null) { + continue; + } + + for (String embedded : bcp.split(",")) { + JarEntry entry = jf.getJarEntry(embedded); + if (entry == null) { + continue; + } + + File tDir = new File(targetDir, file.getName()); + tDir.mkdirs(); + File tFile = new File(tDir, entry.getName()); + + try (InputStream is = jf.getInputStream(entry); + OutputStream os = new FileOutputStream(tFile)) { + is.transferTo(os); + } + result.add(tFile.getAbsolutePath()); + } + } + } + + return result; + } + @Override public int run(LauncherRunContext context, ClassLoader cl) throws Exception { int result = super.run(context, cl); if (result == FrameworkEvent.STOPPED) { File outputDir = new File(Paths.get("").toAbsolutePath().toFile(), "atomos-config"); outputDir.mkdirs(); - try (Reader reader = new FileReader(new File(outputDir, "config-feature.slingosgifeature"), StandardCharsets.UTF_8)) { - Feature config = FeatureJSONReader.read(reader, null); - Feature assembled = FeatureBuilder.assemble(ArtifactId.parse("config:assembled:1.0.0"), new BuilderContext(new FeatureProvider() { - @Override - public Feature provide(ArtifactId id) { - return null; - } - }), m_app, config); - Extension assembledEx = assembled.getExtensions().getByName("atomos-config"); - JsonObject nativeConfig = assembledEx.getJSONStructure().asJsonObject(); - write(outputDir, "reflect-config", nativeConfig); - write(outputDir, "resource-config", nativeConfig); - write(outputDir, "proxy-config", nativeConfig); - write(outputDir, "jni-config", nativeConfig); - write(outputDir, "serialization-config", nativeConfig); - - try (FileOutputStream output = new FileOutputStream(new File(outputDir, "atomos_init.sh"))) { - String script = "#!/bin/sh\n\nexport ATOMOS_CLASSPATH=\""; - if (nativeConfig.containsKey("classpath")) { - script += nativeConfig.getJsonArray("classpath").getValuesAs(JsonString.class).stream().map(JsonString::getString).collect(Collectors.joining(":")); - } - script += "\"\n\nexport ATOMOS_INIT=\""; - if (nativeConfig.containsKey("initialize-at-build-time")) { - script += "--initialize-at-build-time=" + nativeConfig.getJsonArray("initialize-at-build-time").getValuesAs(JsonString.class).stream().map(JsonString::getString).collect(Collectors.joining(",")); + + try (JarOutputStream jarToBuild = getJarToBuild(new File(outputDir, "atomos.substrate.jar"), new File(outputDir, "app.substrate.jar"))) { + try (Reader reader = new FileReader(new File(outputDir, "config-feature.slingosgifeature"), StandardCharsets.UTF_8)) { + Feature config = FeatureJSONReader.read(reader, null); + Feature assembled = FeatureBuilder.assemble(ArtifactId.parse("config:assembled:1.0.0"), new BuilderContext(new FeatureProvider() { + @Override + public Feature provide(ArtifactId id) { + return null; + } + }), m_app, config); + Extension assembledEx = assembled.getExtensions().getByName("atomos-config"); + JsonObject nativeConfig = assembledEx.getJSONStructure().asJsonObject(); + write(outputDir, jarToBuild, "reflect-config", nativeConfig); + write(outputDir, jarToBuild, "resource-config", nativeConfig); + write(outputDir, jarToBuild, "proxy-config", nativeConfig); + write(outputDir, jarToBuild, "jni-config", nativeConfig); + write(outputDir, jarToBuild, "serialization-config", nativeConfig); + + try (FileOutputStream output = new FileOutputStream(new File(outputDir, "atomos_init.sh"))) { + String script = "#!/bin/sh\n\nexport ATOMOS_CLASSPATH=\""; + if (nativeConfig.containsKey("classpath")) { + script += nativeConfig.getJsonArray("classpath").getValuesAs(JsonString.class).stream().map(JsonString::getString).collect(Collectors.joining(":")); + + extractJarsAndCollect(nativeConfig, jarToBuild, outputDir); + } + script += "\"\n\nexport ATOMOS_INIT=\""; + if (nativeConfig.containsKey("initialize-at-build-time")) { + String iabt = "--initialize-at-build-time=" + nativeConfig.getJsonArray("initialize-at-build-time").getValuesAs(JsonString.class).stream().map(JsonString::getString).collect(Collectors.joining(",")); + script += iabt; + + // Add the initialize at build time to a native-image.properties file + addNativeImageProperties(jarToBuild, iabt); + } + script += "\"\n"; + output.write(script.getBytes(StandardCharsets.UTF_8)); } - script += "\"\n"; - output.write(script.getBytes(StandardCharsets.UTF_8)); + + addFeatureToJar(jarToBuild); + } catch (Throwable t) { + t.printStackTrace(); + throw t; } - } catch (Throwable t) { - t.printStackTrace(); - throw t; } - } + + /* */ System.out.println("*** Finished"); + System.exit(0); return result; } - private void write(File outputDir, String name, JsonObject source) throws IOException { + private void addFeatureToJar(JarOutputStream jarToBuild) throws IOException { + JarEntry je = new JarEntry("META-INF/features/feature.json"); + jarToBuild.putNextEntry(je); + try (Writer wr = new OutputStreamWriter(jarToBuild)) { + FeatureJSONWriter.write(wr, m_app); + } + } + + private void extractJarsAndCollect(JsonObject nativeConfig, JarOutputStream jarToBuild, File outputDir) throws IOException { + List<String> jarsToBeCleaned = nativeConfig.getJsonArray("classpath").getValuesAs(JsonString.class).stream().map(JsonString::getString).collect(Collectors.toList()); + List<String> jars = jarsToBeCleaned.stream() + .filter(n -> !n.equals("atomos.substrate.jar")) + .filter(n -> !n.contains("org.apache.felix.framework-")) + .collect(Collectors.toList()); + + List<String> bcpJars = extractBundleClassPathJars(jars, new File(outputDir, "../bcpJars")); + jars.addAll(bcpJars); + + List<URL> jarURLs = nativeConfig.getJsonArray("classpath").getValuesAs(JsonString.class).stream().map(JsonString::getString) + .map(j -> { + try { + return new File(j).toURI().toURL(); + } catch (Exception ex) { + throw new RuntimeException(ex); + }}).collect(Collectors.toList()); + List<URL> bcpJarURLs = bcpJars.stream().map(j -> { + try { + return new File(j).toURI().toURL(); + } catch (Exception ex) { + throw new RuntimeException(ex); + }}).collect(Collectors.toList()); + jarURLs.addAll(bcpJarURLs); + + List<URL> clURLs = new ArrayList<>(jarURLs); + // Temp hack for the org.owasp.esapi.reference.DefaultSecurityConfiguration which references (imports) + // an antiquated commons-lang class but doesn't use it through the code path we are executing. + // It also doesn't import it on the OSGi level. + clURLs.add(new URL("https://repo.maven.apache.org/maven2/commons-lang/commons-lang/2.6/commons-lang-2.6.jar")); + URLClassLoader jarCL = new URLClassLoader(clURLs.toArray(new URL[0])); + + for (String jar : jars) { + try { + addClassesToJar(jar, jarToBuild, jarCL); + } catch (Exception e) { + e.printStackTrace(); + System.exit(-1); + } + } + } + + private void write(File outputDir, JarOutputStream outputJar, String name, JsonObject source) throws IOException { try (FileOutputStream output = new FileOutputStream(new File(outputDir, name + ".json"))) { if (source.containsKey(name)) { Json.createWriter(output).write(source.get(name)); + + JarEntry je = new JarEntry("META-INF/native-image/app/" + name + ".json"); + + outputJar.putNextEntry(je); + Json.createWriter(outputJar).write(source.get(name)); } } } diff --git a/atomosfeaturelauncherconfig/src/main/java/org/apache/sling/feature/launcher/atomos/config/IndexPlugin.java b/atomosfeaturelauncherconfig/src/main/java/org/apache/sling/feature/launcher/atomos/config/IndexPlugin.java index 176d0f93..fff1fbf4 100644 --- a/atomosfeaturelauncherconfig/src/main/java/org/apache/sling/feature/launcher/atomos/config/IndexPlugin.java +++ b/atomosfeaturelauncherconfig/src/main/java/org/apache/sling/feature/launcher/atomos/config/IndexPlugin.java @@ -18,20 +18,8 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package org.apache.sling.feature.launcher.atomos.config; -import org.apache.felix.atomos.utils.api.Context; -import org.apache.felix.atomos.utils.api.FileType; -import org.apache.felix.atomos.utils.api.IndexInfo; -import org.apache.felix.atomos.utils.api.plugin.JarPlugin; -import org.apache.felix.atomos.utils.core.IndexInfoImpl; -import org.apache.felix.atomos.utils.core.plugins.index.IndexOutputType; -import org.apache.felix.atomos.utils.core.plugins.index.IndexPluginConfig; -import org.apache.felix.atomos.utils.substrate.impl.config.DefaultResourceConfiguration; -import org.apache.felix.atomos.utils.substrate.impl.json.ResourceJsonUtil; -import org.osgi.framework.Constants; - import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; @@ -56,6 +44,17 @@ import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.stream.Collectors; +import org.apache.felix.atomos.utils.api.Context; +import org.apache.felix.atomos.utils.api.FileType; +import org.apache.felix.atomos.utils.api.IndexInfo; +import org.apache.felix.atomos.utils.api.plugin.JarPlugin; +import org.apache.felix.atomos.utils.core.IndexInfoImpl; +import org.apache.felix.atomos.utils.core.plugins.index.IndexOutputType; +import org.apache.felix.atomos.utils.core.plugins.index.IndexPluginConfig; +import org.apache.felix.atomos.utils.substrate.impl.config.DefaultResourceConfiguration; +import org.apache.felix.atomos.utils.substrate.impl.json.ResourceJsonUtil; +import org.osgi.framework.Constants; + public class IndexPlugin implements JarPlugin<IndexPluginConfig> { private static final String ATOMOS_BUNDLE_SEPARATOR = "ATOMOS_BUNDLE"; @@ -93,6 +92,7 @@ public class IndexPlugin implements JarPlugin<IndexPluginConfig> { @Override public void initJar(JarFile jar, Context context, URLClassLoader classLoader) { + /* // Detect if there are duplicates using the uniquePaths map jar.stream().filter(this::include).forEach(e -> uniquePaths.compute( e.getName(), @@ -106,6 +106,11 @@ public class IndexPlugin implements JarPlugin<IndexPluginConfig> { || b != null // ? Boolean.FALSE : Boolean.TRUE)); + */ + + jar.stream().filter(this::include).forEach(e -> uniquePaths.compute( + e.getName(), + (p, b) -> Boolean.FALSE)); } @Override diff --git a/atomosfeaturelauncherweaver/src/main/java/org/apache/sling/feature/launcher/atomos/weaver/impl/AtomosWeaverVisitor.java b/atomosfeaturelauncherweaver/src/main/java/org/apache/sling/feature/launcher/atomos/weaver/impl/AtomosWeaverVisitor.java index 349fd8ba..cc072270 100644 --- a/atomosfeaturelauncherweaver/src/main/java/org/apache/sling/feature/launcher/atomos/weaver/impl/AtomosWeaverVisitor.java +++ b/atomosfeaturelauncherweaver/src/main/java/org/apache/sling/feature/launcher/atomos/weaver/impl/AtomosWeaverVisitor.java @@ -18,20 +18,19 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package org.apache.sling.feature.launcher.atomos.weaver.impl; +import java.io.InputStream; +import java.net.URL; + import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; - import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.JSRInlinerAdapter; import org.objectweb.asm.commons.Method; -import java.io.InputStream; -import java.net.URL; - public class AtomosWeaverVisitor extends ClassVisitor implements Opcodes { public static byte[] weave(byte[] bytes, String targetClass, String targetMethodClassLoader, String targetMethodResource, String targetMethodStream, ClassLoader cl) { ClassReader cr = new ClassReader(bytes); @@ -44,10 +43,12 @@ public class AtomosWeaverVisitor extends ClassVisitor implements Opcodes { ),new Method(targetMethodResource, Type.getType(URL.class), new Type[] {Type.getType(Class.class),Type.getType(String.class)} - ),new Method(targetMethodStream, + ),new Method(targetMethodStream, Type.getType(InputStream.class), new Type[] {Type.getType(Class.class), Type.getType(String.class)} - ) + ),new Method("getEntry", + Type.getType(URL.class), + new Type[] {Type.getType(String.class)} ) ); cr.accept(cv, ClassReader.SKIP_FRAMES); if (cv.isWoven()) { @@ -62,13 +63,15 @@ public class AtomosWeaverVisitor extends ClassVisitor implements Opcodes { private final Method targetMethodClassLoader; private final Method targetMethodResource; private final Method targetMethodStream; + private final Method targetMethodBundleResource; - AtomosWeaverVisitor(ClassWriter cv, Type target, Method targetMethodClassLoader, Method targetMethodResource, Method targetMethodStream) { + AtomosWeaverVisitor(ClassWriter cv, Type target, Method targetMethodClassLoader, Method targetMethodResource, Method targetMethodStream, Method targetMethodBundleResource) { super(Opcodes.ASM9, cv); this.target = target; this.targetMethodClassLoader = targetMethodClassLoader; this.targetMethodResource = targetMethodResource; this.targetMethodStream = targetMethodStream; + this.targetMethodBundleResource = targetMethodBundleResource; } boolean isWoven() { @@ -109,6 +112,14 @@ public class AtomosWeaverVisitor extends ClassVisitor implements Opcodes { return; } } + + if (opcode == INVOKEINTERFACE && owner.replace('/', '.').equals("org.osgi.framework.Bundle")) { + if (name.equals("getResource")) { + invokeInterface(Type.getType("L" + owner.replace('.', '/') + ";"), targetMethodBundleResource); + m_woven = true; + return; + } + } super.visitMethodInsn(opcode, owner, name, desc, itf); } }