diff --git a/core-feature-pack/common/src/main/resources/content/bin/common.sh b/core-feature-pack/common/src/main/resources/content/bin/common.sh index d1ef033aa3b..0084a497b70 100755 --- a/core-feature-pack/common/src/main/resources/content/bin/common.sh +++ b/core-feature-pack/common/src/main/resources/content/bin/common.sh @@ -34,7 +34,7 @@ setDefaultModularJvmOptions() { DEFAULT_MODULAR_JVM_OPTIONS=`echo $* | $GREP "\-\-add\-modules"` if [ "x$DEFAULT_MODULAR_JVM_OPTIONS" = "x" ]; then # Set default modular jdk options - # NB: In case an update is made to these exports and opens, make sure that bootable-jar/boot/pom.xml script is in sync. + # NB: In case an update is made to these exports and opens, make sure that bootable-jar/boot/pom.xml and cli/pom.xml script is in sync. # Needed by the iiop-openjdk subsystem DEFAULT_MODULAR_JVM_OPTIONS="$DEFAULT_MODULAR_JVM_OPTIONS --add-exports=java.desktop/sun.awt=ALL-UNNAMED" # Needed to instantiate the default InitialContextFactory implementation used by the diff --git a/core-feature-pack/galleon-common/src/main/resources/packages/tools/pm/wildfly/tasks.xml b/core-feature-pack/galleon-common/src/main/resources/packages/tools/pm/wildfly/tasks.xml index b608d1cf564..a7329b0f44e 100644 --- a/core-feature-pack/galleon-common/src/main/resources/packages/tools/pm/wildfly/tasks.xml +++ b/core-feature-pack/galleon-common/src/main/resources/packages/tools/pm/wildfly/tasks.xml @@ -3,4 +3,10 @@ + + + + + + diff --git a/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java index 9dcd8243538..bb340458783 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/BootableJarCommandBuilder.java @@ -219,7 +219,7 @@ private void setSingleServerArg(final String key, final String value) { * @return a new builder */ public static BootableJarCommandBuilder of(final Path bootableJar) { - return new BootableJarCommandBuilder(Environment.validateBootableJar(bootableJar)); + return new BootableJarCommandBuilder(Environment.validateJar(bootableJar)); } /** @@ -230,7 +230,7 @@ public static BootableJarCommandBuilder of(final Path bootableJar) { * @return a new builder */ public static BootableJarCommandBuilder of(final String bootableJar) { - return new BootableJarCommandBuilder(Environment.validateBootableJar(bootableJar)); + return new BootableJarCommandBuilder(Environment.validateJar(bootableJar)); } /** diff --git a/launcher/src/main/java/org/wildfly/core/launcher/CliCommandBuilder.java b/launcher/src/main/java/org/wildfly/core/launcher/CliCommandBuilder.java index 1617dfd5385..546b5fd906b 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/CliCommandBuilder.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/CliCommandBuilder.java @@ -47,6 +47,8 @@ @SuppressWarnings("unused") public class CliCommandBuilder implements CommandBuilder { + private static Path cliClientJar = Paths.get("bin").resolve("client").resolve("jboss-cli-client.jar"); + enum CliArgument { CONNECT("--connect", "-c"), CONTROLLER("--controller", "controller"), @@ -94,13 +96,17 @@ public static CliArgument find(final Argument argument) { private final Environment environment; private final Arguments javaOpts; private final Arguments cliArgs; + private final boolean modularLauncher; - private CliCommandBuilder(final Environment environment) { + private CliCommandBuilder(final Environment environment, boolean modularLauncher) { this.environment = environment; + this.modularLauncher = modularLauncher; javaOpts = new Arguments(); cliArgs = new Arguments(); - // Add the default logging.properties file - javaOpts.add("-Dlogging.configuration=file:" + environment.resolvePath("bin", "jboss-cli-logging.properties")); + if (modularLauncher) { + // Add the default logging.properties file + javaOpts.add("-Dlogging.configuration=file:" + environment.resolvePath("bin", "jboss-cli-logging.properties")); + } } /** @@ -110,8 +116,8 @@ private CliCommandBuilder(final Environment environment) { * * @return a new builder */ - public static CliCommandBuilder of(final Path wildflyHome) { - return new CliCommandBuilder(new Environment(wildflyHome)); + public static CliCommandBuilder asModularLauncher(final Path wildflyHome) { + return new CliCommandBuilder(new Environment(wildflyHome), true); } /** @@ -121,8 +127,34 @@ public static CliCommandBuilder of(final Path wildflyHome) { * * @return a new builder */ - public static CliCommandBuilder of(final String wildflyHome) { - return new CliCommandBuilder(new Environment(wildflyHome)); + public static CliCommandBuilder asModularLauncher(final String wildflyHome) { + return new CliCommandBuilder(new Environment(wildflyHome), true); + } + + /** + * Creates a command builder for a non-modular CLI instance launched from wildflyHome/bin/client/jboss-client.jar. + * + * @param wildflyHome the path to the WildFly home directory + * + * @return a new builder + */ + public static CliCommandBuilder asJarLauncher(final Path wildflyHome) { + Environment environment = new Environment(wildflyHome); + Environment.validateJar(environment.getWildflyHome().resolve(cliClientJar)); + return new CliCommandBuilder(environment, false); + } + + /** + * Creates a command builder for a non-modular CLI instance launched from wildflyHome/bin/client/jboss-client.jar. + * + * @param wildflyHome the path to the WildFly home directory + * + * @return a new builder + */ + public static CliCommandBuilder asJarLauncher(final String wildflyHome) { + Environment environment = new Environment(wildflyHome); + Environment.validateJar(environment.getWildflyHome().resolve(cliClientJar)); + return new CliCommandBuilder(environment, false); } /** @@ -617,21 +649,29 @@ public Path getJavaHome() { public List buildArguments() { final List cmd = new ArrayList<>(); cmd.addAll(getJavaOptions()); - if (environment.getJvm().isModular()) { - cmd.addAll(AbstractCommandBuilder.DEFAULT_MODULAR_VM_ARGUMENTS); + if (modularLauncher) { + if (environment.getJvm().isModular()) { + cmd.addAll(AbstractCommandBuilder.DEFAULT_MODULAR_VM_ARGUMENTS); + } } if (environment.getJvm().enhancedSecurityManagerAvailable()) { cmd.add(AbstractCommandBuilder.SECURITY_MANAGER_PROP_WITH_ALLOW_VALUE); } cmd.add("-jar"); - cmd.add(environment.getModuleJar().toString()); - cmd.add("-mp"); - cmd.add(getModulePaths()); - cmd.add("org.jboss.as.cli"); - cmd.add("-D" + Environment.HOME_DIR + "=" + environment.getWildflyHome()); + + if (modularLauncher) { + cmd.add(environment.getModuleJar().toString()); + cmd.add("-mp"); + cmd.add(getModulePaths()); + cmd.add("org.jboss.as.cli"); + cmd.add("-D" + Environment.HOME_DIR + "=" + environment.getWildflyHome()); + } else { + cmd.add(environment.getWildflyHome().resolve(cliClientJar).toString()); + } cmd.addAll(cliArgs.asList()); return cmd; + } @Override diff --git a/launcher/src/main/java/org/wildfly/core/launcher/Environment.java b/launcher/src/main/java/org/wildfly/core/launcher/Environment.java index 65c8a642cbe..b06912f2adf 100644 --- a/launcher/src/main/java/org/wildfly/core/launcher/Environment.java +++ b/launcher/src/main/java/org/wildfly/core/launcher/Environment.java @@ -241,18 +241,21 @@ static Path validateWildFlyDir(final Path wildflyHome) { return result; } - static Path validateBootableJar(final Path bootableJar) { - if (bootableJar == null || Files.notExists(bootableJar)) { - throw LauncherMessages.MESSAGES.pathDoesNotExist(bootableJar); + static Path validateJar(final Path jarPath) { + if (jarPath == null || Files.notExists(jarPath)) { + throw LauncherMessages.MESSAGES.pathDoesNotExist(jarPath); } - if (Files.isDirectory(bootableJar)) { - throw LauncherMessages.MESSAGES.pathNotAFile(bootableJar); + if (Files.isDirectory(jarPath)) { + throw LauncherMessages.MESSAGES.pathNotAFile(jarPath); } - final Path result = bootableJar.toAbsolutePath().normalize(); + final Path result = jarPath.toAbsolutePath().normalize(); return result; } - static Path validateBootableJar(final String bootableJar) { - return validateBootableJar(Paths.get(bootableJar)); + static Path validateJar(final String jarPath) { + if (jarPath == null) { + throw LauncherMessages.MESSAGES.pathDoesNotExist(null); + } + return validateJar(Paths.get(jarPath)); } } diff --git a/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java b/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java index 9c8850df31c..1cbf0a90f1d 100644 --- a/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java +++ b/launcher/src/test/java/org/wildfly/core/launcher/CommandBuilderTest.java @@ -199,7 +199,7 @@ public void testDomainBuilder() { @Test public void testCliBuilder() { // Set up a standalone command builder - final CliCommandBuilder commandBuilder = CliCommandBuilder.of(WILDFLY_HOME) + final CliCommandBuilder commandBuilder = CliCommandBuilder.asModularLauncher(WILDFLY_HOME) .addJavaOption("-Djava.net.preferIPv4Stack=true") .addJavaOption("-Djava.net.preferIPv4Stack=false"); diff --git a/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/cli/JbossCliClientJarJPMSSettingsTestCase.java b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/cli/JbossCliClientJarJPMSSettingsTestCase.java new file mode 100644 index 00000000000..883166c3841 --- /dev/null +++ b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/cli/JbossCliClientJarJPMSSettingsTestCase.java @@ -0,0 +1,129 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2022 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * 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.as.test.manualmode.management.cli; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.PropertyPermission; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import org.jboss.as.test.integration.management.cli.CliProcessWrapper; +import org.jboss.as.test.manualmode.management.cli.jpms.JPMSTestActivator; +import org.jboss.as.test.shared.PermissionUtils; +import org.jboss.as.test.shared.TestSuiteEnvironment; +import org.jboss.msc.service.ServiceActivator; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.jboss.shrinkwrap.impl.base.exporter.zip.ZipExporterImpl; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Launches a non-modular CLI client and deploys an application that requires specific JPMS settings. This test case is used to + * verify that jboss-cli-client.jar is able to run properly without requiring the user to add JVM modular settings when it + * launches an embedded server. + */ +public class JbossCliClientJarJPMSSettingsTestCase { + private static final Path SERVER_HOME = Paths.get(TestSuiteEnvironment.getSystemProperty("jboss.dist")); + private static CliProcessWrapper cli; + private static File cliTestAppJar; + + @BeforeClass + public static void setup() throws Exception { + cli = new CliProcessWrapper(false).addCliArgument("--no-color-output").addCliArgument("--timeout=30000"); + cli.executeInteractive(); + cliTestAppJar = createTestArchive("jpms-test-app.jar"); + } + + @AfterClass + public static void cleanup() throws Exception { + cli.destroyProcess(); + Files.deleteIfExists(cliTestAppJar.toPath()); + } + + @Test + public void testManifestEntries() throws Exception { + JarFile clientJar = new JarFile( + SERVER_HOME.resolve("bin").resolve("client").resolve("jboss-cli-client.jar").toString()); + Manifest manifest = clientJar.getManifest(); + Attributes mainAttribs = manifest.getMainAttributes(); + String addOpens = mainAttribs.getValue("Add-Opens"); + Assert.assertTrue("Unexpected value for Manifest Add-Opens. Current: " + addOpens, + addOpens.contains("java.base/java.util")); + } + + @Test + public void testDeploymentRequiringJMPSSettings() throws Exception { + String line = "embed-server --admin-only=false --jboss-home=" + SERVER_HOME.toAbsolutePath(); + cli.pushLineAndWaitForResults(line); + Assert.assertTrue("The CLI message that describes it is running on a non-modular environment was not found: \n" + cli.getOutput(), cli.getOutput().contains("non-modular")); + cli.clearOutput(); + + // ensure there is no deployment yet + cli.pushLineAndWaitForResults("deployment-info --name=" + cliTestAppJar.getName()); + Assert.assertTrue("Apparently the deployment already exists: \n" + cli.getOutput(), cli.getOutput().contains("WFLYCTL0216")); + cli.clearOutput(); + + // ensure that properties added by the deployment are not there + cli.pushLineAndWaitForResults("/core-service=platform-mbean/type=runtime:read-attribute(name=system-properties)"); + Assert.assertTrue(JPMSTestActivator.DEFAULT_SYS_PROP_NAME + " name has been found after getting current System Properties. \n" + cli.getOutput(), !cli.getOutput().contains(JPMSTestActivator.DEFAULT_SYS_PROP_NAME)); + Assert.assertTrue(JPMSTestActivator.DEFAULT_SYS_PROP_VALUE + " value has been found after getting current System Properties. \n" + cli.getOutput(), !cli.getOutput().contains(JPMSTestActivator.DEFAULT_SYS_PROP_VALUE)); + cli.clearOutput(); + + // deploy and verify + cli.pushLineAndWaitForResults("deploy " + cliTestAppJar.getAbsolutePath()); + cli.pushLineAndWaitForResults("deployment-info --name=" + cliTestAppJar.getName()); + Assert.assertTrue("The deployment Failed: \n" + cli.getOutput(), !cli.getOutput().contains("WFLYCTL0216")); + cli.clearOutput(); + + // verifies the activator was executed + cli.pushLineAndWaitForResults("/core-service=platform-mbean/type=runtime:read-attribute(name=system-properties)"); + Assert.assertTrue(JPMSTestActivator.DEFAULT_SYS_PROP_NAME + " name has not been found after getting current System Properties. \n" + cli.getOutput(), cli.getOutput().contains(JPMSTestActivator.DEFAULT_SYS_PROP_NAME)); + Assert.assertTrue(JPMSTestActivator.DEFAULT_SYS_PROP_VALUE + " value has not been found after getting current System Properties. \n" + cli.getOutput(), cli.getOutput().contains(JPMSTestActivator.DEFAULT_SYS_PROP_VALUE)); + cli.clearOutput(); + + // undeploy + cli.pushLineAndWaitForResults("undeploy " + cliTestAppJar.getAbsolutePath()); + cli.pushLineAndWaitForResults("deployment-info --name=" + cliTestAppJar.getName()); + Assert.assertTrue("The undeploy Failed: \n" + cli.getOutput(), cli.getOutput().contains("WFLYCTL0216")); + + cli.pushLineAndWaitForResults("stop-embedded-server"); + } + + public static File createTestArchive(String archiveName) { + JavaArchive jar = ShrinkWrap.create(JavaArchive.class, archiveName); + jar.addClass(JPMSTestActivator.class); + jar.addAsServiceProvider(ServiceActivator.class, JPMSTestActivator.class); + jar.addAsManifestResource(new StringAsset("Dependencies: org.jboss.msc\n"), "MANIFEST.MF"); + jar.addAsManifestResource( + PermissionUtils.createPermissionsXmlAsset(new PropertyPermission("test.deployment.trivial.prop", "write")), + "permissions.xml"); + + final String tempDir = TestSuiteEnvironment.getTmpDir(); + File file = new File(tempDir, jar.getName()); + new ZipExporterImpl(jar).exportTo(file, true); + return file; + } +} diff --git a/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/cli/jpms/JPMSTestActivator.java b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/cli/jpms/JPMSTestActivator.java new file mode 100644 index 00000000000..aeba9921929 --- /dev/null +++ b/testsuite/manualmode/src/test/java/org/jboss/as/test/manualmode/management/cli/jpms/JPMSTestActivator.java @@ -0,0 +1,46 @@ +package org.jboss.as.test.manualmode.management.cli.jpms; + +import java.lang.reflect.Field; +import java.util.EnumMap; + +import org.jboss.msc.Service; +import org.jboss.msc.service.ServiceActivator; +import org.jboss.msc.service.ServiceActivatorContext; +import org.jboss.msc.service.ServiceController; +import org.jboss.msc.service.ServiceName; +import org.jboss.msc.service.ServiceRegistryException; +import org.jboss.msc.service.StartContext; +import org.jboss.msc.service.StartException; +import org.jboss.msc.service.StopContext; + +public class JPMSTestActivator implements ServiceActivator, Service { + public static final ServiceName SERVICE_NAME = ServiceName.of("test", "deployment", "trivial"); + public static final String DEFAULT_SYS_PROP_NAME = "test.deployment.trivial.prop"; + public static final String DEFAULT_SYS_PROP_VALUE = "default-value"; + + @Override + public void activate(ServiceActivatorContext serviceActivatorContext) throws ServiceRegistryException { + // requires opens java.base/java.util + for (Field field : EnumMap.class.getDeclaredFields()) { + if (field.getType() == Class.class) { + field.setAccessible(true); + } + } + + serviceActivatorContext.getServiceTarget() + .addService(SERVICE_NAME) + .setInstance(this) + .setInitialMode(ServiceController.Mode.ACTIVE) + .install(); + } + + @Override + public void start(StartContext context) throws StartException { + System.setProperty(DEFAULT_SYS_PROP_NAME, DEFAULT_SYS_PROP_VALUE); + } + + @Override + public void stop(StopContext context) { + System.clearProperty(DEFAULT_SYS_PROP_NAME); + } +} diff --git a/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessBuilder.java b/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessBuilder.java index 814b39352d7..2a956d684f8 100644 --- a/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessBuilder.java +++ b/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessBuilder.java @@ -48,10 +48,20 @@ abstract class CliProcessBuilder{ private CliCommandBuilder cliCommandBuilder; private String cliConfigFile; - public CliProcessBuilder(){ + /** + * Creates the CLI Process launched as a modular application or as non-modular application, in which case the CLI process + * is launched by using jboss-cli-client.jar + * + * @param modular Whether the process will be launched as a modular application. + */ + public CliProcessBuilder(boolean modular){ String jbossDist = TestSuiteEnvironment.getSystemProperty("jboss.dist"); - cliCommandBuilder = CliCommandBuilder.of(jbossDist); - cliCommandBuilder.addJavaOptions(System.getProperty("cli.jvm.args", "").split("\\s+")); + if (modular) { + cliCommandBuilder = CliCommandBuilder.asModularLauncher(jbossDist); + cliCommandBuilder.addJavaOptions(System.getProperty("cli.jvm.args", "").split("\\s+")); + } else { + cliCommandBuilder = CliCommandBuilder.asJarLauncher(jbossDist); + } } public CliProcessWrapper addCliArgument(String argument){ diff --git a/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessWrapper.java b/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessWrapper.java index 894449cfd68..e5a4952cbea 100644 --- a/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessWrapper.java +++ b/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/cli/CliProcessWrapper.java @@ -40,6 +40,12 @@ public class CliProcessWrapper extends CliProcessBuilder { public CliProcessWrapper(){ + super(true); + cliProcessWrapper = this; + } + + public CliProcessWrapper(boolean modular){ + super(modular); cliProcessWrapper = this; } diff --git a/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/util/CustomCLIExecutor.java b/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/util/CustomCLIExecutor.java index 2c0da81e6b3..e6a5e7dedb6 100644 --- a/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/util/CustomCLIExecutor.java +++ b/testsuite/shared/src/main/java/org/jboss/as/test/integration/management/util/CustomCLIExecutor.java @@ -104,7 +104,7 @@ public static String execute(File cliConfigFile, String operation, String contro } - final CliCommandBuilder commandBuilder = CliCommandBuilder.of(jbossDist) + final CliCommandBuilder commandBuilder = CliCommandBuilder.asModularLauncher(jbossDist) .setModuleDirs(modulePath.split(Pattern.quote(File.pathSeparator))); final List ipv6Args = new ArrayList<>(); @@ -205,7 +205,7 @@ public static String executeOffline(final boolean logFailure, final Iterable