diff --git a/biz.aQute.bndall.tests/test/biz/aQute/launcher/AlsoLauncherTest.java b/biz.aQute.bndall.tests/test/biz/aQute/launcher/AlsoLauncherTest.java index 3edd3c571f..a13c9d552a 100644 --- a/biz.aQute.bndall.tests/test/biz/aQute/launcher/AlsoLauncherTest.java +++ b/biz.aQute.bndall.tests/test/biz/aQute/launcher/AlsoLauncherTest.java @@ -96,6 +96,64 @@ public void tearDown() throws Exception { System.setProperties(prior); } + @Test + public void testLocationFormat() throws Exception { + project.setProperty(Constants.RUNPROPERTIES, "test.cmd=exit"); + project.setProperty(Constants.RUNTRACE, "true"); + project.setProperty("-executable", "location='${@bsn}'"); + Entry export = project.export("bnd.executablejar", null); + assertThat(export).isNotNull(); + + try (Jar jar = new Jar(".", export.getValue() + .openInputStream())) { + + assertThat(jar.getResources() + .keySet()).contains(// + "jar/biz.aQute.launcher.jar", // -runpath + "jar/org.apache.felix.framework-5.6.10.jar", // -runpath + "jar/apiguardian-api-1.1.0.jar", // not a bundle + "jar/demo", // + // the following were not a bundle yet + // "jar/junit-jupiter-api", // + // "jar/junit-jupiter-engine", // + // "jar/junit-jupiter-params", // + // "jar/junit-platform-commons", // + // "jar/junit-platform-engine", // + // "jar/junit-vintage-engine", // + "jar/org.apache.felix.configadmin", // + "jar/org.apache.felix.scr", // + "jar/org.apache.servicemix.bundles.junit", // + "jar/org.opentest4j" // + ); + + File tmp = File.createTempFile("foo", ".jar"); + try { + + jar.write(tmp); + Command cmd = new Command(); + cmd.add(project.getJavaExecutable("java")); + cmd.add("-jar"); + cmd.add(tmp.getAbsolutePath()); + + StringBuilder stdout = new StringBuilder(); + StringBuilder stderr = new StringBuilder(); + int execute = cmd.execute(stdout, stderr); + String output = stdout.append(stderr) + .toString(); + System.out.println(output); + + // These must be bsns ow + assertThat(output).contains("installing jar/org.apache.felix.scr", + "installing jar/org.apache.felix.configadmin"); + + assertThat(execute).isEqualTo(42); + + } finally { + tmp.delete(); + } + } + } + /** * Test that the Bndrun file is loaded when we create a run * diff --git a/biz.aQute.launcher/src/aQute/launcher/plugin/ProjectLauncherImpl.java b/biz.aQute.launcher/src/aQute/launcher/plugin/ProjectLauncherImpl.java index 4891bcd03c..696cc9b5e4 100644 --- a/biz.aQute.launcher/src/aQute/launcher/plugin/ProjectLauncherImpl.java +++ b/biz.aQute.launcher/src/aQute/launcher/plugin/ProjectLauncherImpl.java @@ -32,10 +32,13 @@ import aQute.bnd.build.Container; import aQute.bnd.build.Project; import aQute.bnd.build.ProjectLauncher; +import aQute.bnd.header.Attrs; +import aQute.bnd.header.OSGiHeader; import aQute.bnd.header.Parameters; import aQute.bnd.help.instructions.LauncherInstructions.RunOption; import aQute.bnd.osgi.Builder; import aQute.bnd.osgi.Constants; +import aQute.bnd.osgi.Domain; import aQute.bnd.osgi.EmbeddedResource; import aQute.bnd.osgi.FileResource; import aQute.bnd.osgi.Instructions; @@ -44,6 +47,7 @@ import aQute.bnd.osgi.JarResource; import aQute.bnd.osgi.Processor; import aQute.bnd.osgi.Resource; +import aQute.bnd.osgi.Verifier; import aQute.launcher.constants.LauncherConstants; import aQute.lib.collections.MultiMap; import aQute.lib.io.ByteBufferDataInput; @@ -357,7 +361,8 @@ public Jar executable() throws Exception { if (!file.isFile()) getProject().error("Invalid entry in -runbundles %s", file); else { - String newPath = nonCollidingPath(file, jar); + + String newPath = nonCollidingPath(file, jar, getExecutableLocation()); jar.putResource(newPath, getJarFileResource(file, rejar, strip)); actualPaths.add(newPath); } @@ -425,6 +430,24 @@ public Jar executable() throws Exception { return jar; } + /** + * Temp method until we can use the 5.1 interface parsing + * + * @return location or null + */ + private String getExecutableLocation() { + String executableString = getProject().getProperty("-executable"); + if (executableString == null) + return null; + + try { + Attrs executable = OSGiHeader.parseProperties(executableString); + return executable.get("location"); + } catch (Exception e) { + return null; + } + } + private Map> extractStripMapping(List strip) { MultiMap map = new MultiMap<>(); @@ -506,6 +529,53 @@ String nonCollidingPath(File file, Jar jar) throws Exception { return path; } + String nonCollidingPath(File file, Jar jar, String locationFormat) throws Exception { + if (locationFormat == null) + return nonCollidingPath(file, jar); + + try { + Domain domain = Domain.domain(file); + Entry bundleSymbolicName = domain.getBundleSymbolicName(); + if (bundleSymbolicName == null) { + warning("Cannot find bsn in %s, required because it is on the -runbundles", file); + return nonCollidingPath(file, jar, null); + } + String bundleVersion = domain.getBundleVersion(); + if (bundleVersion == null) + bundleVersion = "0"; + else { + if (!Verifier.isVersion(bundleVersion)) { + error("Invalid bundle version in %s", file); + return nonCollidingPath(file, jar, null); + } + } + try (Processor p = new Processor(this)) { + + p.setProperty("@bsn", bundleSymbolicName.getKey()); + p.setProperty("@version", bundleVersion); + p.setProperty("@name", file.getName()); + + String fileName = p.getReplacer() + .process(locationFormat); + + if (fileName.contains("/")) { + error("Invalid bundle version in %s", file); + return nonCollidingPath(file, jar, null); + } + + String filePath = "jar/" + fileName; + if (jar.getResources() + .containsKey(filePath)) { + error("Duplicate locations for %s for file %s", filePath, file); + } + return filePath; + } + } catch (Exception e) { + exception(e, "failed to use location pattern %s for location for file %", locationFormat, file); + return nonCollidingPath(file, jar); + } + } + /* * Useful for when exported as folder or unzipped */