You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2020/09/04 16:43:08 UTC

[karaf] branch master updated: 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)

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 <rm...@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 {
+    }
 }