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