This is an automated email from the ASF dual-hosted git repository. jbonofre pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/karaf.git
The following commit(s) were added to refs/heads/master by this push: new 84273d8 Enhance RunMojo to let it configure main args, console log level, system properties and be able to deploy a bundle before/after the features with local lookup instead of assuming the artifact is attached to the project (karaf:run without any other goal) new 2973e54 This closes #1169 84273d8 is described below commit 84273d897a0b8035e2369db05c3743da0a30e350 Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Fri Aug 28 09:40:47 2020 +0200 Enhance RunMojo to let it configure main args, console log level, system properties and be able to deploy a bundle before/after the features with local lookup instead of assuming the artifact is attached to the project (karaf:run without any other goal) --- .../java/org/apache/karaf/tooling/RunMojo.java | 112 +++++++++++++-- .../java/org/apache/karaf/tooling/RunMojoTest.java | 153 +++++++++++++++++---- 2 files changed, 227 insertions(+), 38 deletions(-) diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java index feeb878..8aa6ba0 100644 --- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java +++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/RunMojo.java @@ -48,6 +48,8 @@ import java.lang.reflect.Method; import java.net.URI; import java.nio.file.Files; import java.util.List; +import java.util.Map; +import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -78,6 +80,22 @@ public class RunMojo extends MojoSupport { private boolean deployProjectArtifact = true; /** + * If set and the artifact is not attached to the project, this location will be used. + * It enables to launch <code>karaf:run</code> without building/attaching the artifact. + * A typical good value is + * {@code <fallbackLocalProjectArtifact>${project.build.directory}/${project.build.finalName}.jar</fallbackLocalProjectArtifact>}. + */ + @Parameter + private File fallbackLocalProjectArtifact; + + /** + * If true project and <code>deployProjectArtifact</code> is true, + * artifact is deployed after the feature installation, otherwise before. + */ + @Parameter(defaultValue = "false") + private boolean deployAfterFeatures = false; + + /** * A list of URLs referencing feature repositories that will be added * to the karaf instance started by this goal. */ @@ -85,6 +103,22 @@ public class RunMojo extends MojoSupport { private String[] featureRepositories = null; /** + * Karaf main args. + */ + @Parameter + private String[] mainArgs; + + /** + * Karaf console log level + * (<code>karaf.log.console</code> value used in default karaf logging configuration). + */ + @Parameter + private String consoleLogLevel; + + @Parameter + private Map<String, String> systemProperties; + + /** * Comma-separated list of features to install. */ @Parameter(defaultValue = "") @@ -111,6 +145,13 @@ public class RunMojo extends MojoSupport { private static final Pattern mvnPattern = Pattern.compile("mvn:([^/ ]+)/([^/ ]+)/([^/ ]*)(/([^/ ]+)(/([^/ ]+))?)?"); public void execute() throws MojoExecutionException, MojoFailureException { + // reset system properties after the execution to ensure not not pollute the maven build + final Properties originalProperties = new Properties(); + originalProperties.putAll(System.getProperties()); + + // before any mkdir or so since "clean" is handled + final String[] args = handleArgs(karafDirectory, mainArgs == null ? new String[0] : mainArgs); + if (karafDirectory.exists()) { getLog().info("Using Karaf container located " + karafDirectory.getAbsolutePath()); } else { @@ -130,9 +171,18 @@ public class RunMojo extends MojoSupport { System.setProperty("karaf.etc", karafDirectory.getAbsolutePath() + "/etc"); System.setProperty("karaf.log", karafDirectory.getAbsolutePath() + "/data/log"); System.setProperty("karaf.instances", karafDirectory.getAbsolutePath() + "/instances"); - System.setProperty("karaf.startLocalConsole", "false"); + if (System.getProperty("karaf.startLocalConsole") == null) { + System.setProperty("karaf.startLocalConsole", "false"); + } System.setProperty("karaf.startRemoteShell", startSsh); System.setProperty("karaf.lock", "false"); + if (consoleLogLevel != null && !consoleLogLevel.isEmpty()) { + System.setProperty("karaf.log.console", consoleLogLevel); + } + // last to ensure it wins over defaults/shortcuts + if (systemProperties != null) { + systemProperties.forEach(System::setProperty); + } String featureBootFinished = BootFinished.class.getName(); Thread bootThread = Thread.currentThread(); @@ -147,12 +197,7 @@ public class RunMojo extends MojoSupport { return super.loadClass(name, resolve); } }; - Main main = new Main(new String[0]) { - @Override - protected ClassLoader getParentClassLoader() { - return bootLoader; - } - }; + final Main main = newMain(bootLoader, args); try { long start = System.nanoTime(); @@ -204,8 +249,13 @@ public class RunMojo extends MojoSupport { Object featureService = findFeatureService(featureBundleCtx); addFeatureRepositories(featureService); - deploy(featureBundleCtx, featureService); + if (!deployAfterFeatures) { + deploy(featureBundleCtx, featureService); + } addFeatures(featureService); + if (deployAfterFeatures) { + deploy(featureBundleCtx, featureService); + } if (keepRunning) main.awaitShutdown(); main.destroy(); @@ -213,9 +263,44 @@ public class RunMojo extends MojoSupport { throw new MojoExecutionException("Can't start container", e); } finally { System.gc(); + System.getProperties().clear(); + System.getProperties().putAll(originalProperties); } } + private String[] handleArgs(final File base, final String[] strings) { + return Stream.of(strings) + .filter(it -> { + switch (it) { + case "console": + System.setProperty("karaf.startLocalConsole", "true"); + return false; + case "clean": + if (base.exists()) { + getLog().info("Cleaning " + base); + try { + FileUtils.deleteDirectory(base); + } catch (final IOException e) { // assuming it failed on win + getLog().error(e.getMessage(), e); + } + } + return false; + default: + return true; + } + }) + .toArray(String[]::new); + } + + protected Main newMain(final ClassLoader bootLoader, final String[] args) { + return new Main(args) { + @Override + protected ClassLoader getParentClassLoader() { + return bootLoader; + } + }; + } + // todo: maybe add it as a mojo parameter to reduce it for light distro? private void waitForValidState() throws InterruptedException { Thread.sleep(1000); @@ -248,7 +333,7 @@ public class RunMojo extends MojoSupport { void deploy(BundleContext bundleContext, Object featureService) throws MojoExecutionException { if (deployProjectArtifact) { - File artifact = project.getArtifact().getFile(); + File artifact = getProjectArtifact(); File attachedFeatureFile = getAttachedFeatureFile(project); boolean artifactExists = artifact != null && artifact.exists(); if (!artifactExists) { @@ -274,6 +359,15 @@ public class RunMojo extends MojoSupport { } } + private File getProjectArtifact() { + final File file = project.getArtifact().getFile(); + if ((file == null || !file.exists()) && + fallbackLocalProjectArtifact != null && fallbackLocalProjectArtifact.exists()) { + return fallbackLocalProjectArtifact; + } + return file; + } + void addFeatures(Object featureService) throws MojoExecutionException { if (featuresToInstall != null) { try { diff --git a/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/RunMojoTest.java b/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/RunMojoTest.java index b584a7e..6b8b9c2 100644 --- a/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/RunMojoTest.java +++ b/tooling/karaf-maven-plugin/src/test/java/org/apache/karaf/tooling/RunMojoTest.java @@ -16,27 +16,87 @@ */ package org.apache.karaf.tooling; +import static java.util.Collections.singletonMap; import static org.junit.Assert.*; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.stream.Stream; import org.apache.karaf.features.FeaturesService; +import org.apache.karaf.main.Main; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import static org.easymock.EasyMock.*; import org.easymock.EasyMock; import org.easymock.EasyMockSupport; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.ServiceReference; public class RunMojoTest extends EasyMockSupport { + @Rule + public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void testArgs() throws IllegalAccessException, + MojoFailureException, MojoExecutionException, IOException { + final AtomicReference<String[]> capturedArgs = new AtomicReference<>(); + final RunMojo mojo = newRunMojo(args -> { + capturedArgs.set(args); + throw new FastExit(); + }); + setPrivateField(mojo, "mainArgs", new String[]{"foo"}); + try { + mojo.execute(); + } catch (final FastExit fe) { + // expected + } + assertArrayEquals(new String[]{"foo"}, capturedArgs.get()); + } + + @Test + public void testSystemProperties() throws IllegalAccessException, + MojoFailureException, MojoExecutionException, IOException { + final RunMojo mojo = newRunMojo(args -> { + throw new FastExit(); + }); + setPrivateField(mojo, "systemProperties", singletonMap("RunMojoTest.testSystemProperties", "set")); + try { + mojo.execute(); + } catch (final FastExit fe) { + // expected + } + assertEquals("set", System.clearProperty("RunMojoTest.testSystemProperties")); + } + + @Test + public void testConsoleLevel() throws IllegalAccessException, + MojoFailureException, MojoExecutionException, IOException { + final RunMojo mojo = newRunMojo(args -> { + throw new FastExit(); + }); + setPrivateField(mojo, "consoleLogLevel", "INFO"); + try { + mojo.execute(); + } catch (final FastExit fe) { + // expected + } + assertEquals("INFO", System.clearProperty("karaf.log.console")); + } @Test public void testAddFeatureRepositoriesWithNullRepoList() throws MojoExecutionException { @@ -49,7 +109,7 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testAddFeatureRepositoriesWithEmptyRepoListAndNullFeatureService() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, MojoExecutionException { + public void testAddFeatureRepositoriesWithEmptyRepoListAndNullFeatureService() throws SecurityException, IllegalArgumentException, IllegalAccessException, MojoExecutionException { RunMojo mojo = new RunMojo(); String[] empty = new String[0]; setPrivateField(mojo, "featureRepositories", empty); @@ -62,7 +122,7 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testAddFeatureRepositoriesWithEmptyRepoList() throws MojoExecutionException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + public void testAddFeatureRepositoriesWithEmptyRepoList() throws MojoExecutionException, SecurityException, IllegalArgumentException, IllegalAccessException { FeaturesService featureService = mock(FeaturesService.class); replay(featureService); @@ -88,7 +148,7 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testDeployWithDeployProjectArtifactFalse() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, MojoExecutionException { + public void testDeployWithDeployProjectArtifactFalse() throws SecurityException, IllegalArgumentException, IllegalAccessException, MojoExecutionException { BundleContext context = mock(BundleContext.class); RunMojo mojo = new RunMojo(); setPrivateField(mojo, "deployProjectArtifact", false); @@ -96,13 +156,13 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testDeployWithNullArtifact() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + public void testDeployWithNullArtifact() throws SecurityException, IllegalArgumentException, IllegalAccessException { BundleContext context = mock(BundleContext.class); Artifact artifact = mock(Artifact.class); RunMojo mojo = new RunMojo(); MavenProject project = new MavenProject(); project.setArtifact(artifact); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); try { mojo.deploy(context, null); fail("Expected MojoExecutionException"); @@ -112,16 +172,18 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testDeployWithNonExistingArtifact() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + public void testDeployWithNonExistingArtifact() throws SecurityException, IllegalArgumentException, IllegalAccessException { BundleContext context = mock(BundleContext.class); Artifact artifact = mock(Artifact.class); File artifactFile = mock(File.class); expect(artifact.getFile()).andReturn(artifactFile); + expect(artifactFile.exists()).andReturn(false).times(2); + replay(artifactFile); replay(artifact); RunMojo mojo = new RunMojo(); MavenProject project = new MavenProject(); project.setArtifact(artifact); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); try { mojo.deploy(context, null); fail("Expected MojoExecutionException"); @@ -131,18 +193,18 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testDeployWithExistingArtifactButProjectNotBundle() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + public void testDeployWithExistingArtifactButProjectNotBundle() throws SecurityException, IllegalArgumentException, IllegalAccessException { BundleContext context = mock(BundleContext.class); Artifact artifact = mock(Artifact.class); File artifactFile = mock(File.class); - expect(artifactFile.exists()).andReturn(true); + expect(artifactFile.exists()).andReturn(true).times(2); replay(artifactFile); expect(artifact.getFile()).andReturn(artifactFile); replay(artifact); RunMojo mojo = new RunMojo(); MavenProject project = new MavenProject(); project.setArtifact(artifact); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); try { mojo.deploy(context, null); fail("Expected MojoExecutionException"); @@ -152,11 +214,11 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testDeployWithExistingArtifactFailsInInstall() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + public void testDeployWithExistingArtifactFailsInInstall() throws SecurityException, IllegalArgumentException, IllegalAccessException { BundleContext context = mock(BundleContext.class); Artifact artifact = mock(Artifact.class); File artifactFile = niceMock(File.class); - expect(artifactFile.exists()).andReturn(true); + expect(artifactFile.exists()).andReturn(true).times(2); replay(artifactFile); expect(artifact.getFile()).andReturn(artifactFile); replay(artifact); @@ -164,7 +226,7 @@ public class RunMojoTest extends EasyMockSupport { MavenProject project = new MavenProject(); project.setPackaging("bundle"); project.setArtifact(artifact); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); try { mojo.deploy(context, null); fail("Expected MojoExecutionException"); @@ -174,7 +236,7 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testDeployWithExistingArtifact() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException, BundleException, MojoExecutionException { + public void testDeployWithExistingArtifact() throws SecurityException, IllegalArgumentException, IllegalAccessException, IOException, BundleException, MojoExecutionException { BundleContext context = niceMock(BundleContext.class); Bundle bundle = niceMock(Bundle.class); expect(context.installBundle(anyString())).andReturn(bundle); @@ -188,7 +250,7 @@ public class RunMojoTest extends EasyMockSupport { MavenProject project = new MavenProject(); project.setPackaging("bundle"); project.setArtifact(artifact); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); replay(bundle); mojo.deploy(context, null); verify(bundle); @@ -198,7 +260,7 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testDeployWithPomArtifactAndAttachedFeatureXmlNoFeatureService() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException, BundleException, MojoExecutionException { + public void testDeployWithPomArtifactAndAttachedFeatureXmlNoFeatureService() throws SecurityException, IllegalArgumentException, IllegalAccessException, IOException, BundleException, MojoExecutionException { File artifactFeaturesAttachmentFile = File.createTempFile("someproject-features", ".xml"); try { BundleContext context = niceMock(BundleContext.class); @@ -218,7 +280,7 @@ public class RunMojoTest extends EasyMockSupport { project.setPackaging("pom"); project.setArtifact(artifact); project.addAttachedArtifact(artifactFeaturesAttachment); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); replay(bundle); try { mojo.deploy(context, null); @@ -258,7 +320,7 @@ public class RunMojoTest extends EasyMockSupport { project.setPackaging("pom"); project.setArtifact(artifact); project.addAttachedArtifact(artifactFeaturesAttachment); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); try { mojo.deploy(context, featureService); fail("Expected MojoExecutionException"); @@ -295,7 +357,7 @@ public class RunMojoTest extends EasyMockSupport { project.setPackaging("pom"); project.setArtifact(artifact); project.addAttachedArtifact(artifactFeaturesAttachment); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); mojo.deploy(context, featureService); verify(featureService); } finally { @@ -328,7 +390,7 @@ public class RunMojoTest extends EasyMockSupport { project.setPackaging("pom"); project.setArtifact(artifact); project.addAttachedArtifact(artifactFeaturesAttachment); - setInheritedPrivateField(mojo, "project", project); + setPrivateField(mojo, "project", project); setPrivateField(mojo, "featuresToInstall", "liquibase-core, ukelonn-db-derby-test, ukelonn"); String[] featureRepos = { "mvn:org.ops4j.pax.jdbc/pax-jdbc-features/LATEST/xml/features" }; setPrivateField(mojo, "featureRepositories", featureRepos); @@ -369,7 +431,7 @@ public class RunMojoTest extends EasyMockSupport { } @Test - public void testAddFeaturesNullFeatureService() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + public void testAddFeaturesNullFeatureService() throws SecurityException, IllegalArgumentException, IllegalAccessException { RunMojo mojo = new RunMojo(); setPrivateField(mojo, "featuresToInstall", "liquibase-core, ukelonn-db-derby-test, ukelonn"); @@ -419,16 +481,49 @@ public class RunMojoTest extends EasyMockSupport { assertNotNull(service); } - private void setPrivateField(Object obj, String fieldName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { - Field field = obj.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - field.set(obj, value); + private RunMojo newRunMojo(final Function<String[], Main> mainFactory) + throws IllegalAccessException, IOException { + final Path base = temporaryFolder.getRoot().toPath(); + Stream.of("config.properties", "jre.properties").forEach(etc -> { + final Path configProperties = base.resolve("etc").resolve(etc); + try { + Files.createDirectories(configProperties.getParent()); + Files.copy( + Paths.get("../../main/src/test/resources/test-karaf-home/etc").resolve(etc), + configProperties); + } catch (final IOException e) { + fail(e.getMessage()); + } + }); + Files.createDirectories(base.resolve("system")); + final RunMojo mojo = new RunMojo() { + @Override + protected Main newMain(final ClassLoader bootLoader, final String[] args) { + if (mainFactory == null) { + return super.newMain(bootLoader, args); + } + return mainFactory.apply(args); + } + }; + setPrivateField(mojo, "karafDirectory", temporaryFolder.getRoot()); + return mojo; } - private void setInheritedPrivateField(Object obj, String fieldName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { - Field field = obj.getClass().getSuperclass().getDeclaredField(fieldName); - field.setAccessible(true); - field.set(obj, value); + private void setPrivateField(Object obj, String fieldName, Object value) throws SecurityException, IllegalArgumentException, IllegalAccessException { + Class<?> aClass = obj.getClass(); + while (aClass != null) { + try { + Field field = aClass.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(obj, value); + return; + } catch (final NoSuchFieldException nsfe) { + aClass = aClass.getSuperclass(); + } + } + fail("cant set " + fieldName); } + private static class FastExit extends RuntimeException { + } }