diff --git a/client/pom.xml b/client/pom.xml
index 12451587..49217609 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -40,6 +40,10 @@
org.jboss.fuse.mvnd
mvnd-common
+
+ org.apache.maven
+ maven-embedder
+
org.slf4j
slf4j-simple
diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonConnector.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonConnector.java
index 60978d78..465d619c 100644
--- a/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonConnector.java
+++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DaemonConnector.java
@@ -223,7 +223,7 @@ public class DaemonConnector {
public DaemonClientConnection startDaemon(DaemonCompatibilitySpec constraint) {
final String daemon = UUID.randomUUID().toString();
- final Process process = startDaemon(daemon);
+ final Process process = startDaemon(daemon, constraint.getOptions());
LOGGER.debug("Started Maven daemon {}", daemon);
long start = System.currentTimeMillis();
do {
@@ -241,7 +241,7 @@ public class DaemonConnector {
throw new DaemonException.ConnectException("Timeout waiting to connect to the Maven daemon.\n" + diag.describe());
}
- private Process startDaemon(String uid) {
+ private Process startDaemon(String uid, List opts) {
final Path mavenHome = layout.mavenHome();
final Path workingDir = layout.userDir();
String command = "";
@@ -266,6 +266,7 @@ public class DaemonConnector {
args.add("-Xmx4g");
args.add(Environment.DAEMON_IDLE_TIMEOUT_MS.asCommandLineProperty(Integer.toString(layout.getIdleTimeoutMs())));
args.add(Environment.DAEMON_KEEP_ALIVE_MS.asCommandLineProperty(Integer.toString(layout.getKeepAliveMs())));
+ args.addAll(opts);
args.add(MavenDaemon.class.getName());
command = String.join(" ", args);
diff --git a/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java b/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java
index 410bd895..c97256bc 100644
--- a/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java
+++ b/client/src/main/java/org/jboss/fuse/mvnd/client/DefaultClient.java
@@ -15,14 +15,25 @@
*/
package org.jboss.fuse.mvnd.client;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import org.apache.maven.cli.internal.extension.model.CoreExtension;
+import org.apache.maven.cli.internal.extension.model.io.xpp3.CoreExtensionsXpp3Reader;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.fusesource.jansi.Ansi;
import org.jboss.fuse.mvnd.common.BuildProperties;
import org.jboss.fuse.mvnd.common.DaemonCompatibilitySpec;
@@ -44,9 +55,13 @@ import org.slf4j.LoggerFactory;
public class DefaultClient implements Client {
- private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClient.class);
public static final int DEFAULT_PERIODIC_CHECK_INTERVAL_MILLIS = 10 * 1000;
public static final int CANCEL_TIMEOUT = 10 * 1000;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DefaultClient.class);
+ private static final String EXT_CLASS_PATH = "maven.ext.class.path";
+ private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml";
+
private final Supplier lazyLayout;
private final BuildProperties buildProperties;
@@ -198,8 +213,8 @@ public class DefaultClient implements Client {
args.add("-Dmaven.repo.local=" + localMavenRepository.toString());
}
+ List opts = getDaemonOpts(layout);
final DaemonConnector connector = new DaemonConnector(layout, registry, buildProperties);
- List opts = new ArrayList<>();
try (DaemonClientConnection daemon = connector.connect(new DaemonCompatibilitySpec(javaHome, opts), output)) {
output.buildStatus("Connected to daemon");
@@ -245,6 +260,56 @@ public class DefaultClient implements Client {
}
}
+ private List getDaemonOpts(ClientLayout layout) {
+ List options = new ArrayList<>();
+ // Classpath
+ List jars = parseExtClasspath(layout);
+ if (!jars.isEmpty()) {
+ options.add(Environment.DAEMON_EXT_CLASSPATH.asCommandLineProperty(
+ jars.stream().map(Path::toString).collect(Collectors.joining(","))));
+ }
+ // Extensions
+ try {
+ List extensions = readCoreExtensionsDescriptor(layout);
+ if (!extensions.isEmpty()) {
+ options.add(Environment.DAEMON_CORE_EXTENSIONS.asCommandLineProperty(
+ extensions.stream().map(e -> e.getGroupId() + ":" + e.getArtifactId() + ":" + e.getVersion())
+ .collect(Collectors.joining(","))));
+ }
+ } catch (IOException | XmlPullParserException e) {
+ throw new RuntimeException("Unable to parse core extensions", e);
+ }
+ return options;
+ }
+
+ private List parseExtClasspath(ClientLayout layout) {
+ String extClassPath = System.getProperty(EXT_CLASS_PATH);
+ List jars = new ArrayList<>();
+ if (StringUtils.isNotEmpty(extClassPath)) {
+ for (String jar : StringUtils.split(extClassPath, File.pathSeparator)) {
+ Path path = layout.userDir().resolve(jar).toAbsolutePath();
+ jars.add(path);
+ }
+ }
+ return jars;
+ }
+
+ private List readCoreExtensionsDescriptor(ClientLayout layout)
+ throws IOException, XmlPullParserException {
+ Path multiModuleProjectDirectory = layout.multiModuleProjectDirectory();
+ if (multiModuleProjectDirectory == null) {
+ return Collections.emptyList();
+ }
+ Path extensionsFile = multiModuleProjectDirectory.resolve(EXTENSIONS_FILENAME);
+ if (!Files.exists(extensionsFile)) {
+ return Collections.emptyList();
+ }
+ CoreExtensionsXpp3Reader parser = new CoreExtensionsXpp3Reader();
+ try (InputStream is = Files.newInputStream(extensionsFile)) {
+ return parser.read(is).getExtensions();
+ }
+ }
+
static void setDefaultArgs(List args, ClientLayout layout) {
if (args.stream().noneMatch(arg -> arg.startsWith("-T") || arg.equals("--threads"))) {
args.add("-T" + layout.getThreads());
diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonCompatibilitySpec.java b/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonCompatibilitySpec.java
index 5d5ab8e3..0aa8bcbe 100644
--- a/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonCompatibilitySpec.java
+++ b/common/src/main/java/org/jboss/fuse/mvnd/common/DaemonCompatibilitySpec.java
@@ -39,6 +39,14 @@ public class DaemonCompatibilitySpec {
this.options = Objects.requireNonNull(options, "options");
}
+ public Path getJavaHome() {
+ return javaHome;
+ }
+
+ public List getOptions() {
+ return options;
+ }
+
public Result isSatisfiedBy(DaemonInfo daemon) {
if (!javaHomeMatches(daemon)) {
return new Result(false, () -> "Java home is different.\n" + diff(daemon));
diff --git a/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java b/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java
index d2715d35..2f558459 100644
--- a/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java
+++ b/common/src/main/java/org/jboss/fuse/mvnd/common/Environment.java
@@ -57,7 +57,15 @@ public enum Environment {
* line.
*/
MVND_THREADS("mvnd.threads", null),
- DAEMON_UID("daemon.uid", null);
+ DAEMON_UID("daemon.uid", null),
+ /**
+ * Internal option to specify the maven extension classpath
+ */
+ DAEMON_EXT_CLASSPATH("daemon.ext.classpath", null),
+ /**
+ * Internal option to specify the list of maven extension to register
+ */
+ DAEMON_CORE_EXTENSIONS("daemon.core.extensions", null);
public static final int DEFAULT_IDLE_TIMEOUT = (int) TimeUnit.HOURS.toMillis(3);
diff --git a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
index 2bb3ea06..278f0a2b 100644
--- a/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
+++ b/daemon/src/main/java/org/apache/maven/cli/DaemonMavenCli.java
@@ -52,6 +52,8 @@ import org.apache.maven.building.Source;
import org.apache.maven.cli.configuration.ConfigurationProcessor;
import org.apache.maven.cli.configuration.SettingsXmlConfigurationProcessor;
import org.apache.maven.cli.event.ExecutionEventLogger;
+import org.apache.maven.cli.internal.BootstrapCoreExtensionManager;
+import org.apache.maven.cli.internal.extension.model.CoreExtension;
import org.apache.maven.cli.logging.Slf4jLoggerManager;
import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
import org.apache.maven.cli.transfer.QuietMavenTransferListener;
@@ -90,6 +92,7 @@ import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.transfer.TransferListener;
+import org.jboss.fuse.mvnd.common.Environment;
import org.jboss.fuse.mvnd.logging.smart.AbstractLoggingSpy;
import org.jboss.fuse.mvnd.plugin.CliPluginRealmCache;
import org.slf4j.ILoggerFactory;
@@ -121,6 +124,10 @@ public class DaemonMavenCli {
public static final File DEFAULT_GLOBAL_TOOLCHAINS_FILE = new File(System.getProperty("maven.conf"), "toolchains.xml");
+ private static final String EXT_CLASS_PATH = "maven.ext.class.path";
+
+ private static final String EXTENSIONS_FILENAME = ".mvn/extensions.xml";
+
private static final String MVN_MAVEN_CONFIG = ".mvn/maven.config";
public static final String STYLE_COLOR_PROPERTY = "style.color";
@@ -168,6 +175,7 @@ public class DaemonMavenCli {
cli(cliRequest);
properties(cliRequest);
logging(cliRequest);
+ container(cliRequest);
configure(cliRequest);
version(cliRequest);
toolchains(cliRequest);
@@ -390,6 +398,17 @@ public class DaemonMavenCli {
populateProperties(cliRequest.commandLine, cliRequest.systemProperties, cliRequest.userProperties);
}
+ void container(CliRequest cliRequest) {
+ Map data = new HashMap<>();
+ data.put("plexus", container);
+ data.put("workingDirectory", cliRequest.workingDirectory);
+ data.put("systemProperties", cliRequest.systemProperties);
+ data.put("userProperties", cliRequest.userProperties);
+ data.put("versionProperties", CLIReportingUtils.getBuildProperties());
+ eventSpyDispatcher.init(() -> data);
+
+ }
+
void container()
throws Exception {
ClassRealm coreRealm = classWorld.getClassRealm("plexus.core");
@@ -397,13 +416,28 @@ public class DaemonMavenCli {
coreRealm = classWorld.getRealms().iterator().next();
}
- // List extClassPath = parseExtClasspath( cliRequest );
+ List extClassPath = Stream
+ .of(Environment.DAEMON_EXT_CLASSPATH.systemProperty().orDefault(() -> "").asString().split(","))
+ .map(File::new)
+ .collect(Collectors.toList());
CoreExtensionEntry coreEntry = CoreExtensionEntry.discoverFrom(coreRealm);
- List extensions = Collections.emptyList();
- // loadCoreExtensions( cliRequest, coreRealm, coreEntry.getExportedArtifacts() );
- ClassRealm containerRealm = coreRealm;
+ List extensions = Stream
+ .of(Environment.DAEMON_CORE_EXTENSIONS.systemProperty().orDefault(() -> "").asString().split(","))
+ .filter(s -> s != null && !s.isEmpty())
+ .map(s -> {
+ String[] parts = s.split(":");
+ CoreExtension ce = new CoreExtension();
+ ce.setGroupId(parts[0]);
+ ce.setArtifactId(parts[1]);
+ ce.setVersion(parts[2]);
+ return ce;
+ })
+ .collect(Collectors.toList());
+ List extensionsEntries = loadCoreExtensions(extensions, coreRealm,
+ coreEntry.getExportedArtifacts());
+ ClassRealm containerRealm = setupContainerRealm(classWorld, coreRealm, extClassPath, extensionsEntries);
ContainerConfiguration cc = new DefaultContainerConfiguration().setClassWorld(classWorld)
.setRealm(containerRealm).setClassPathScanning(PlexusConstants.SCANNING_INDEX).setAutoWiring(true)
@@ -411,11 +445,10 @@ public class DaemonMavenCli {
Set exportedArtifacts = new HashSet<>(coreEntry.getExportedArtifacts());
Set exportedPackages = new HashSet<>(coreEntry.getExportedPackages());
- // for ( CoreExtensionEntry extension : extensions )
- // {
- // exportedArtifacts.addAll( extension.getExportedArtifacts() );
- // exportedPackages.addAll( extension.getExportedPackages() );
- // }
+ for (CoreExtensionEntry extension : extensionsEntries) {
+ exportedArtifacts.addAll(extension.getExportedArtifacts());
+ exportedPackages.addAll(extension.getExportedPackages());
+ }
final CoreExports exports = new CoreExports(containerRealm, exportedArtifacts, exportedPackages);
final CliPluginRealmCache realmCache = new CliPluginRealmCache();
@@ -435,7 +468,7 @@ public class DaemonMavenCli {
container.setLoggerManager(plexusLoggerManager);
- for (CoreExtensionEntry extension : extensions) {
+ for (CoreExtensionEntry extension : extensionsEntries) {
container.discoverComponents(extension.getClassRealm(), new SessionScopeModule(container),
new MojoExecutionScopeModule(container));
}
@@ -454,7 +487,108 @@ public class DaemonMavenCli {
toolchainsBuilder = container.lookup(ToolchainsBuilder.class);
dispatcher = (DefaultSecDispatcher) container.lookup(SecDispatcher.class, "maven");
+ }
+ private List loadCoreExtensions(List extensions, ClassRealm containerRealm,
+ Set providedArtifacts) {
+ try {
+ if (extensions.isEmpty()) {
+ return Collections.emptyList();
+ }
+ ContainerConfiguration cc = new DefaultContainerConfiguration() //
+ .setClassWorld(classWorld) //
+ .setRealm(containerRealm) //
+ .setClassPathScanning(PlexusConstants.SCANNING_INDEX) //
+ .setAutoWiring(true) //
+ .setJSR250Lifecycle(true) //
+ .setName("maven");
+
+ DefaultPlexusContainer container = new DefaultPlexusContainer(cc, new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(ILoggerFactory.class).toInstance(slf4jLoggerFactory);
+ }
+ });
+
+ try {
+ CliRequest cliRequest = new CliRequest(new String[0], classWorld);
+ cliRequest.commandLine = new CommandLine.Builder().build();
+ container.setLookupRealm(null);
+ container.setLoggerManager(plexusLoggerManager);
+ container.getLoggerManager().setThresholds(cliRequest.request.getLoggingLevel());
+ Thread.currentThread().setContextClassLoader(container.getContainerRealm());
+ executionRequestPopulator = container.lookup(MavenExecutionRequestPopulator.class);
+ configurationProcessors = container.lookupMap(ConfigurationProcessor.class);
+ configure(cliRequest);
+ populateRequest(cliRequest, cliRequest.request);
+ executionRequestPopulator.populateDefaults(cliRequest.request);
+ BootstrapCoreExtensionManager resolver = container.lookup(BootstrapCoreExtensionManager.class);
+ return Collections
+ .unmodifiableList(resolver.loadCoreExtensions(cliRequest.request, providedArtifacts, extensions));
+ } finally {
+ executionRequestPopulator = null;
+ container.dispose();
+ }
+ } catch (RuntimeException e) {
+ // runtime exceptions are most likely bugs in maven, let them bubble up to the user
+ throw e;
+ } catch (Exception e) {
+ slf4jLogger.warn("Failed to load extensions descriptor {}: {}", extensions, e.getMessage());
+ }
+ return Collections.emptyList();
+ }
+
+ private ClassRealm setupContainerRealm(ClassWorld classWorld, ClassRealm coreRealm, List extClassPath,
+ List extensions) throws Exception {
+ if (!extClassPath.isEmpty() || !extensions.isEmpty()) {
+ ClassRealm extRealm = classWorld.newRealm("maven.ext", null);
+ extRealm.setParentRealm(coreRealm);
+ slf4jLogger.debug("Populating class realm {}", extRealm.getId());
+ for (File file : extClassPath) {
+
+ extRealm.addURL(file.toURI().toURL());
+ }
+ for (CoreExtensionEntry entry : reverse(extensions)) {
+ Set exportedPackages = entry.getExportedPackages();
+ ClassRealm realm = entry.getClassRealm();
+ for (String exportedPackage : exportedPackages) {
+ extRealm.importFrom(realm, exportedPackage);
+ }
+ if (exportedPackages.isEmpty()) {
+ // sisu uses realm imports to establish component visibility
+ extRealm.importFrom(realm, realm.getId());
+ }
+ }
+ return extRealm;
+ }
+ return coreRealm;
+ }
+
+ private static List reverse(List list) {
+ List copy = new ArrayList<>(list);
+ Collections.reverse(copy);
+ return copy;
+ }
+
+ private List parseExtClasspath(CliRequest cliRequest) {
+ String extClassPath = cliRequest.userProperties.getProperty(EXT_CLASS_PATH);
+ if (extClassPath == null) {
+ extClassPath = cliRequest.systemProperties.getProperty(EXT_CLASS_PATH);
+ }
+
+ List jars = new ArrayList<>();
+
+ if (StringUtils.isNotEmpty(extClassPath)) {
+ for (String jar : StringUtils.split(extClassPath, File.pathSeparator)) {
+ File file = resolveFile(new File(jar), cliRequest.workingDirectory);
+
+ slf4jLogger.debug(" Included {}", file);
+
+ jars.add(file);
+ }
+ }
+
+ return jars;
}
//
@@ -669,14 +803,6 @@ public class DaemonMavenCli {
//
cliRequest.request.setEventSpyDispatcher(eventSpyDispatcher);
- Map data = new HashMap<>();
- data.put("plexus", container);
- data.put("workingDirectory", cliRequest.workingDirectory);
- data.put("systemProperties", cliRequest.systemProperties);
- data.put("userProperties", cliRequest.userProperties);
- data.put("versionProperties", CLIReportingUtils.getBuildProperties());
- eventSpyDispatcher.init(() -> data);
-
//
// We expect at most 2 implementations to be available. The SettingsXmlConfigurationProcessor implementation
// is always available in the core and likely always will be, but we may have another ConfigurationProcessor
@@ -797,7 +923,10 @@ public class DaemonMavenCli {
}
private void populateRequest(CliRequest cliRequest) {
- MavenExecutionRequest request = cliRequest.request;
+ populateRequest(cliRequest, cliRequest.request);
+ }
+
+ private void populateRequest(CliRequest cliRequest, MavenExecutionRequest request) {
CommandLine commandLine = cliRequest.commandLine;
String workingDirectory = cliRequest.workingDirectory;
boolean quiet = cliRequest.quiet;
diff --git a/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/Server.java b/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/Server.java
index 6682f09c..d66efbe0 100644
--- a/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/Server.java
+++ b/daemon/src/main/java/org/jboss/fuse/mvnd/daemon/Server.java
@@ -101,6 +101,10 @@ public class Server implements AutoCloseable, Runnable {
strategy = DaemonExpiration.master();
List opts = new ArrayList<>();
+ Environment.DAEMON_EXT_CLASSPATH.systemProperty().asOptional()
+ .ifPresent(s -> opts.add(Environment.DAEMON_EXT_CLASSPATH.asCommandLineProperty(s)));
+ Environment.DAEMON_CORE_EXTENSIONS.systemProperty().asOptional()
+ .ifPresent(s -> opts.add(Environment.DAEMON_CORE_EXTENSIONS.asCommandLineProperty(s)));
long cur = System.currentTimeMillis();
final Path javaHome = Paths.get(System.getProperty("mvnd.java.home"));
info = new DaemonInfo(uid, javaHome.toString(), layout.mavenHome().toString(),
diff --git a/dist/src/main/provisio/maven-distro.xml b/dist/src/main/provisio/maven-distro.xml
index 9811b3fe..07c5f60e 100644
--- a/dist/src/main/provisio/maven-distro.xml
+++ b/dist/src/main/provisio/maven-distro.xml
@@ -61,7 +61,7 @@
-
+
diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/DistroIT.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/DistroIT.java
index c64018c3..4c6028af 100644
--- a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/DistroIT.java
+++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/DistroIT.java
@@ -47,9 +47,8 @@ public class DistroIT {
.collect(Collectors.toList());
final String msg = mavenHome.resolve("mvn/lib/ext") + " contains duplicates available in "
- + mavenHome.resolve("mvn/lib")
- + " or " + mavenHome.resolve("mvn/lib");
- Assertions.assertEquals(new ArrayList(), dups, msg);
+ + mavenHome.resolve("mvn/lib") + " or " + mavenHome.resolve("mvn/boot");
+ Assertions.assertEquals(new ArrayList(), dups, msg);
}
@Test
diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsNativeIT.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsNativeIT.java
new file mode 100644
index 00000000..f62b8e40
--- /dev/null
+++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsNativeIT.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.jboss.fuse.mvnd.it;
+
+import java.io.IOException;
+import java.util.Collections;
+import javax.inject.Inject;
+import org.assertj.core.api.Assertions;
+import org.jboss.fuse.mvnd.client.Client;
+import org.jboss.fuse.mvnd.client.ClientLayout;
+import org.jboss.fuse.mvnd.common.DaemonInfo;
+import org.jboss.fuse.mvnd.common.logging.ClientOutput;
+import org.jboss.fuse.mvnd.junit.MvndNativeTest;
+import org.jboss.fuse.mvnd.junit.TestRegistry;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@MvndNativeTest(projectDir = "src/test/projects/extensions")
+public class ExtensionsNativeIT {
+
+ @Inject
+ Client client;
+
+ @Inject
+ ClientLayout layout;
+
+ @Inject
+ TestRegistry registry;
+
+ @Test
+ void version() throws IOException, InterruptedException {
+ registry.killAll();
+ Assertions.assertThat(registry.getAll().size()).isEqualTo(0);
+
+ final ClientOutput o = Mockito.mock(ClientOutput.class);
+ client.execute(o, "-v").assertSuccess();
+ Assertions.assertThat(registry.getAll().size()).isEqualTo(1);
+ DaemonInfo daemon = registry.getAll().iterator().next();
+ assertEquals(Collections.singletonList("-Ddaemon.core.extensions=io.takari.aether:takari-local-repository:0.11.3"),
+ daemon.getOptions());
+
+ client.execute(o, "-v").assertSuccess();
+ Assertions.assertThat(registry.getAll().size()).isEqualTo(1);
+ }
+
+}
diff --git a/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsTest.java b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsTest.java
new file mode 100644
index 00000000..4e01e998
--- /dev/null
+++ b/integration-tests/src/test/java/org/jboss/fuse/mvnd/it/ExtensionsTest.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019 the original author or authors.
+ *
+ * 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.jboss.fuse.mvnd.it;
+
+import org.jboss.fuse.mvnd.junit.MvndTest;
+
+@MvndTest(projectDir = "src/test/projects/extensions")
+public class ExtensionsTest extends ExtensionsNativeIT {
+
+}
diff --git a/integration-tests/src/test/projects/extensions/.mvn/extensions.xml b/integration-tests/src/test/projects/extensions/.mvn/extensions.xml
new file mode 100644
index 00000000..a9e180cd
--- /dev/null
+++ b/integration-tests/src/test/projects/extensions/.mvn/extensions.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ io.takari.aether
+ takari-local-repository
+ 0.11.3
+
+
diff --git a/integration-tests/src/test/projects/extensions/pom.xml b/integration-tests/src/test/projects/extensions/pom.xml
new file mode 100644
index 00000000..1f7a43b9
--- /dev/null
+++ b/integration-tests/src/test/projects/extensions/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ org.jboss.fuse.mvnd.test.extensions
+ extensions
+ 0.0.1-SNAPSHOT
+ pom
+
+
\ No newline at end of file