scripts = new ArrayList<>();
+
+ /**
+ * The path to the server configuration to use.
+ */
+ @Parameter(alias = "server-config", property = PropertyNames.SERVER_CONFIG)
+ private String serverConfig;
+
+ /**
+ * Additional extensions of files located in {@code src/webapp} directory and its sub-directories that don't
+ * require a redeployment on update.
+ *
+ * The builtin list is {@code html, xhtml, jsp, js, css}.
+ *
+ *
+ * You can set the system property {@code wildfly.dev.web.extensions} to a white space separated list of file
+ * extensions.
+ *
+ */
+ @Parameter(property = "wildfly.dev.web.extensions", alias = "web-extensions")
+ private List webExtensions = new ArrayList<>();
+
+ /**
+ * File patterns that we should ignore during watch.
+ *
+ * Hidden files and files ending with '~' are ignored.
+ *
+ *
+ * You can set the system property {@code wildfly.dev.ignore.patterns} to a white space separated list of file
+ * patterns.
+ *
+ */
+ @Parameter(property = "wildfly.dev.ignore.patterns", alias = "ignore-patterns")
+ private List ignorePatterns = new ArrayList<>();
+ // Lazily loaded list of patterns based on the ignorePatterns
+ private final List ignoreUpdatePatterns = new ArrayList<>();
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ final var packageType = PackageType.resolve(project);
+ if (!"war".equals(packageType.getPackaging())) {
+ throw new MojoExecutionException("The dev goal only works for WAR deployments");
+ }
+ // Start the container
+ final ServerContext context = startServer(ServerType.STANDALONE);
+ // Do we need to build first?
+ if (needsCompile()) {
+ triggerResources();
+ triggerCompile();
+ triggerExplodeWar();
+ }
+ try (final WatchService watcher = FileSystems.getDefault().newWatchService()) {
+ final CompiledSourceHandler sourceHandler = new CompiledSourceHandler();
+ registerDir(watcher, Path.of(project.getBuild().getSourceDirectory()), sourceHandler);
+ for (Resource resource : project.getResources()) {
+ registerDir(watcher, Path.of(resource.getDirectory()), new ResourceHandler());
+ }
+ registerDir(watcher, resolveWebAppSourceDir(), new WebAppResourceHandler(webExtensions));
+ try (ModelControllerClient client = createClient()) {
+ // Execute commands before the deployment is done
+ final CommandConfiguration cmdConfig = CommandConfiguration.of(this::createClient, this::getClientConfiguration)
+ .addCommands(commands)
+ .addScripts(scripts)
+ .setJBossHome(context.jbossHome())
+ .setFork(true)
+ .setStdout("none")
+ .setTimeout(timeout)
+ .build();
+ commandExecutor.execute(cmdConfig, mavenRepoManager);
+ final DeploymentManager deploymentManager = DeploymentManager.Factory.create(client);
+ final Deployment deployment = getDeploymentContent();
+ try {
+ final DeploymentResult result = deploymentManager.forceDeploy(deployment);
+ if (!result.successful()) {
+ throw new MojoExecutionException("Failed to deploy content: " + result.getFailureMessage());
+ }
+ watch(watcher, deploymentManager, deployment);
+ } finally {
+ deploymentManager.undeploy(UndeployDescription.of(deployment));
+ ServerHelper.shutdownStandalone(client);
+ }
+ }
+ } catch (IOException e) {
+ throw new MojoExecutionException(e.getLocalizedMessage(), e);
+ } finally {
+ context.process().destroyForcibly();
+ }
+ }
+
+ @Override
+ public String goal() {
+ return "dev";
+ }
+
+ @Override
+ protected CommandBuilder createCommandBuilder(final Path jbossHome) throws MojoExecutionException {
+ return createStandaloneCommandBuilder(jbossHome, serverConfig);
+ }
+
+ /**
+ * Allows the {@linkplain #webExtensions} to be set as a string.
+ *
+ * @param webExtensions a whitespace delimited string for the web file extensions
+ */
+ @SuppressWarnings("unused")
+ public void setWebExtensions(final String webExtensions) {
+ this.webExtensions = Utils.splitArguments(webExtensions);
+ }
+
+ /**
+ * Allows the {@linkplain #ignorePatterns} to be set as a string.
+ *
+ * @param ignorePatterns a whitespace delimited string for the file patterns
+ */
+ @SuppressWarnings("unused")
+ public void setIgnorePatterns(final String ignorePatterns) {
+ this.ignorePatterns = Utils.splitArguments(ignorePatterns);
+ }
+
+ private boolean registerDir(final WatchService watcher, final Path dir, final WatchHandler handler) throws IOException {
+ if (Files.exists(dir) && Files.isDirectory(dir)) {
+ final int currentSize = watchedDirectories.size();
+ final Set registered = watchedDirectories.values()
+ .stream()
+ .map(WatchContext::directory)
+ .collect(Collectors.toCollection(HashSet::new));
+ Files.walkFileTree(dir, new SimpleFileVisitor<>() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ if (!project.getBuild().getOutputDirectory().equals(dir.toString())) {
+ if (registered.add(dir)) {
+ final WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
+ watchedDirectories.put(key, WatchContext.of(dir, handler));
+ debug("Watching for changes in %s", dir);
+ }
+ return FileVisitResult.CONTINUE;
+ } else {
+ return FileVisitResult.SKIP_SUBTREE;
+ }
+ }
+ });
+ return currentSize == watchedDirectories.size();
+ }
+ return false;
+ }
+
+ @SuppressWarnings("InfiniteLoopStatement")
+ private void watch(final WatchService watcher, final DeploymentManager deploymentManager, final Deployment deployment) {
+ final var projectDir = project.getBasedir().toPath();
+ try {
+ for (; ; ) {
+ WatchKey key = watcher.take();
+ for (WatchEvent> event : key.pollEvents()) {
+ WatchEvent.Kind> kind = event.kind();
+ if (kind == OVERFLOW) {
+ continue;
+ }
+ @SuppressWarnings("unchecked")
+ final WatchEvent ev = (WatchEvent) event;
+ debug("File changed [%s]: %s", ev.kind().name(), ev.context());
+ final Path absolutePath = getPath(key, ev.context());
+ if (absolutePath == null) {
+ continue;
+ }
+ final var eventPath = absolutePath.getFileName();
+ try {
+ if (isIgnoredChange(eventPath)) {
+ debug("Ignoring change for %s", eventPath);
+ continue;
+ }
+ } catch (IOException ex) {
+ debug("Failed checking %s for ignored state: %s", eventPath, ex);
+ }
+ try {
+ final var context = watchedDirectories.get(key);
+ if (context == null) {
+ getLog().warn(String.format("Failed to find context for %s", ev.context()));
+ continue;
+ }
+ final var relativePath = projectDir.relativize(absolutePath);
+ if (ev.kind() == ENTRY_DELETE) {
+ // Undeploy application as Windows won't be able to delete the directory
+ DeploymentResult deploymentResult = deploymentManager.undeploy(UndeployDescription.of(deployment));
+ if (!deploymentResult.successful()) {
+ getLog().warn(String.format("Failed to undeploy application. Unexpected results may occur. Failure: %s",
+ deploymentResult.getFailureMessage()));
+ } else {
+ // Clean the deployment directory
+ final Path path = resolveWarDir();
+ deleteRecursively(path);
+ triggerResources();
+ triggerCompile();
+ triggerExplodeWar();
+ deploymentResult = deploymentManager.deploy(deployment);
+ if (!deploymentResult.successful()) {
+ throw new MojoExecutionException("Failed to deploy content: " + deploymentResult.getFailureMessage());
+ }
+ if (Files.notExists(context.directory())) {
+ watchedDirectories.remove(key);
+ key.cancel();
+ }
+ continue;
+ }
+ } else if (ev.kind() == ENTRY_CREATE) {
+ // If this is a directory we need to add the directory
+ if (Files.isDirectory(eventPath)) {
+ if (registerDir(watcher, eventPath, context.handler())) {
+ debug("New directory registered: %s", relativePath);
+ }
+ } else {
+ final Path parent = absolutePath.getParent();
+ if (parent != null) {
+ if (registerDir(watcher, parent, context.handler())) {
+ debug("New directory registered: %s", relativePath);
+ }
+ }
+ debug("A new source file has been created: %s", relativePath);
+ }
+ } else if (ev.kind() == ENTRY_MODIFY) {
+ debug("Source file modified: %s", relativePath);
+ }
+ // Handle the file
+ final var result = context.handle(ev, absolutePath);
+ if (result.requiresRecompile()) {
+ triggerCompile();
+ }
+ if (result.requiresCopyResources()) {
+ triggerResources();
+ }
+ if (result.requiresRepackage()) {
+ triggerExplodeWar();
+ }
+ if (result.requiresRedeploy()) {
+ final DeploymentResult deploymentResult = deploymentManager.redeployToRuntime(deployment);
+ if (!deploymentResult.successful()) {
+ throw new MojoExecutionException("Failed to deploy content: " + deploymentResult.getFailureMessage());
+ }
+ }
+ } catch (Exception ex) {
+ getLog().error("Exception handling file change: " + ex);
+ }
+ }
+ key.reset();
+ }
+ } catch (ClosedWatchServiceException ex) {
+ // OK Can ignore, we have been closed by shutdown hook.
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted during watch.", e);
+ }
+ }
+
+ private void triggerCompile() throws MojoExecutionException {
+ // Compile the Java sources if needed
+ final String compilerPluginKey = ORG_APACHE_MAVEN_PLUGINS + ":" + MAVEN_COMPILER_PLUGIN;
+ final Plugin compilerPlugin = project.getPlugin(compilerPluginKey);
+ if (compilerPlugin != null) {
+ executeGoal(project, compilerPlugin, ORG_APACHE_MAVEN_PLUGINS, MAVEN_COMPILER_PLUGIN, MAVEN_COMPILER_GOAL, getPluginConfig(compilerPlugin, MAVEN_COMPILER_GOAL));
+ }
+ }
+
+ private void triggerExplodeWar() throws MojoExecutionException {
+ // Compile the Java sources if needed
+ final String warPluginKey = ORG_APACHE_MAVEN_PLUGINS + ":" + MAVEN_WAR_PLUGIN;
+ final Plugin warPlugin = project.getPlugin(warPluginKey);
+ if (warPlugin != null) {
+ executeGoal(project, warPlugin, ORG_APACHE_MAVEN_PLUGINS, MAVEN_WAR_PLUGIN, MAVEN_EXPLODED_GOAL, getPluginConfig(warPlugin));
+ } else {
+ getLog().warn("Can't package war application, war plugin not found");
+ }
+ }
+
+ private void triggerResources() throws MojoExecutionException {
+ List resources = project.getResources();
+ if (resources.isEmpty()) {
+ return;
+ }
+ Plugin resourcesPlugin = project.getPlugin(ORG_APACHE_MAVEN_PLUGINS + ":" + MAVEN_RESOURCES_PLUGIN);
+ if (resourcesPlugin == null) {
+ return;
+ }
+ executeGoal(project, resourcesPlugin, ORG_APACHE_MAVEN_PLUGINS, MAVEN_RESOURCES_PLUGIN, MAVEN_RESOURCES_GOAL, getPluginConfig(resourcesPlugin, MAVEN_RESOURCES_GOAL));
+ }
+
+ private Path getPath(final WatchKey key, final Path fileName) {
+ final WatchContext context = watchedDirectories.get(key);
+ if (context == null) {
+ getLog().debug("No more watching key, ignoring change done to " + fileName);
+ return null;
+ } else {
+ final Path resolved = context.directory().resolve(fileName);
+ // Fully ignore target dir
+ if (Path.of(project.getBuild().getDirectory()).equals(resolved)) {
+ return null;
+ }
+ return resolved;
+ }
+
+ }
+
+ private boolean needsCompile() {
+ // Check if compiling is going to be done by a previous goal
+ boolean compileNeeded = true;
+ for (String goal : mavenSession.getGoals()) {
+ if (POST_COMPILE_PHASES.contains(goal)) {
+ compileNeeded = false;
+ break;
+ }
+ if (goal.endsWith("wildfly:" + goal())) {
+ break;
+ }
+ }
+ return compileNeeded;
+ }
+
+ private void executeGoal(final MavenProject project, final Plugin plugin, final String groupId, final String artifactId, final String goal, final Xpp3Dom config) throws MojoExecutionException {
+ executeMojo(plugin(groupId(groupId), artifactId(artifactId), version(plugin.getVersion()), plugin.getDependencies()), MojoExecutor.goal(goal), config, executionEnvironment(project, mavenSession, pluginManager));
+ }
+
+ private Xpp3Dom getPluginConfig(final Plugin plugin, final String goal) throws MojoExecutionException {
+ Xpp3Dom mergedConfig = null;
+ if (!plugin.getExecutions().isEmpty()) {
+ for (PluginExecution exec : plugin.getExecutions()) {
+ if (exec.getConfiguration() != null && exec.getGoals().contains(goal)) {
+ mergedConfig = mergedConfig == null ? (Xpp3Dom) exec.getConfiguration() : Xpp3Dom.mergeXpp3Dom(mergedConfig, (Xpp3Dom) exec.getConfiguration(), true);
+ }
+ }
+ }
+
+ if (plugin.getConfiguration() != null) {
+ mergedConfig = mergedConfig == null ? (Xpp3Dom) plugin.getConfiguration() : Xpp3Dom.mergeXpp3Dom(mergedConfig, (Xpp3Dom) plugin.getConfiguration(), true);
+ }
+
+ final Xpp3Dom configuration = configuration();
+
+ if (mergedConfig != null) {
+ Set supportedParams = null;
+ // Filter out `test*` configurations
+ for (Xpp3Dom child : mergedConfig.getChildren()) {
+ if (child.getName().startsWith("test")) {
+ continue;
+ }
+ if (supportedParams == null) {
+ supportedParams = getMojoDescriptor(plugin, goal).getParameterMap().keySet();
+ }
+ if (supportedParams.contains(child.getName())) {
+ configuration.addChild(child);
+ }
+ }
+ }
+
+ return configuration;
+ }
+
+ private Xpp3Dom getPluginConfig(final Plugin plugin) {
+
+ final Xpp3Dom configuration = configuration();
+
+ final Xpp3Dom pluginConfiguration = (Xpp3Dom) plugin.getConfiguration();
+ if (pluginConfiguration != null) {
+ //Filter out `test*` configurations
+ for (Xpp3Dom child : pluginConfiguration.getChildren()) {
+ if (!child.getName().startsWith("test") && !child.getName().startsWith("failOnMissingWebXml")) {
+ configuration.addChild(child);
+ }
+ }
+ }
+
+ final MojoExecutor.Element e = new MojoExecutor.Element("webappDirectory", resolveWarDir().toAbsolutePath()
+ .toString());
+ configuration.addChild(e.toDom());
+ return configuration;
+ }
+
+ // Required to retrieve the actual set of supported configuration items.
+ private MojoDescriptor getMojoDescriptor(Plugin plugin, String goal) throws MojoExecutionException {
+ try {
+ return pluginManager.getMojoDescriptor(plugin, goal, repositories, session);
+ } catch (Exception e) {
+ throw new MojoExecutionException("Failed to obtain descriptor for Maven plugin " + plugin.getId() + " goal " + goal, e);
+ }
+ }
+
+ private boolean isIgnoredChange(final Path file) throws IOException {
+ if (isHiddenFile(file) || file.getFileName().toString().endsWith("~")) {
+ return true;
+ }
+ for (Pattern pattern : getPatterns()) {
+ if (pattern.matcher(file.getFileName().toString()).matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isHiddenFile(final Path p) throws IOException {
+ if (Environment.isWindows()) {
+ final DosFileAttributes dosAttrs = Files.readAttributes(p, DosFileAttributes.class);
+ return dosAttrs.isHidden();
+ } else {
+ return Files.isHidden(p);
+ }
+ }
+
+ private List getPatterns() {
+ if (!ignorePatterns.isEmpty()) {
+ if (ignoreUpdatePatterns.isEmpty()) {
+ for (String p : ignorePatterns) {
+ Pattern pattern = Pattern.compile(p);
+ ignoreUpdatePatterns.add(pattern);
+ }
+ }
+ }
+ return ignoreUpdatePatterns;
+ }
+
+ private Deployment getDeploymentContent() {
+ final PackageType packageType = PackageType.resolve(project);
+ final Deployment deployment = Deployment.of(resolveWarDir());
+ final String filename;
+ //if (this.filename == null) {
+ filename = String.format("%s.%s", project.getBuild().getFinalName(), packageType.getFileExtension());
+ //} else {
+ //filename = this.filename;
+ //}
+ return deployment.setRuntimeName(filename);
+ }
+
+ private Path resolveWebAppSourceDir() {
+ final String warPluginKey = ORG_APACHE_MAVEN_PLUGINS + ":" + MAVEN_WAR_PLUGIN;
+ final Plugin warPlugin = project.getPlugin(warPluginKey);
+ Xpp3Dom dom = getPluginConfig(warPlugin);
+ final Xpp3Dom warSourceDirectory = dom.getChild("warSourceDirectory");
+ if (warSourceDirectory == null) {
+ return project.getBasedir().toPath().resolve("src").resolve("main").resolve("webapp");
+ }
+ return Path.of(warSourceDirectory.getValue());
+ }
+
+ private Path resolveWarDir() {
+ return Path.of(project.getBuild().getDirectory()).resolve(project.getBuild().getFinalName());
+ }
+
+ private void debug(final String format, final Object... args) {
+ getLog().debug(String.format("[WATCH] " + format, args));
+ }
+
+ private static void deleteRecursively(final Path path) throws IOException {
+ if (Files.isDirectory(path)) {
+ Files.walkFileTree(path, new SimpleFileVisitor<>() {
+
+ @Override
+ public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
+ Files.delete(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ } else if (Files.exists(path)) {
+ Files.delete(path);
+ }
+ }
+}
diff --git a/plugin/src/main/java/org/wildfly/plugin/dev/ResourceHandler.java b/plugin/src/main/java/org/wildfly/plugin/dev/ResourceHandler.java
new file mode 100644
index 00000000..fe88349f
--- /dev/null
+++ b/plugin/src/main/java/org/wildfly/plugin/dev/ResourceHandler.java
@@ -0,0 +1,50 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2023 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.wildfly.plugin.dev;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+
+/**
+ * @author James R. Perkins
+ */
+class ResourceHandler implements WatchHandler {
+
+ @Override
+ public Result handle(final WatchContext context, final WatchEvent event, final Path file) throws IOException {
+ return new Result() {
+ @Override
+ public boolean requiresCopyResources() {
+ return true;
+ }
+
+ @Override
+ public boolean requiresRepackage() {
+ return true;
+ }
+
+ @Override
+ public boolean requiresRedeploy() {
+ return true;
+ }
+ };
+ }
+}
diff --git a/plugin/src/main/java/org/wildfly/plugin/dev/WatchContext.java b/plugin/src/main/java/org/wildfly/plugin/dev/WatchContext.java
new file mode 100644
index 00000000..f3ef6966
--- /dev/null
+++ b/plugin/src/main/java/org/wildfly/plugin/dev/WatchContext.java
@@ -0,0 +1,61 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2023 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.wildfly.plugin.dev;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+
+import org.apache.maven.plugin.MojoExecutionException;
+
+/**
+ * @author James R. Perkins
+ */
+class WatchContext implements Comparable {
+ private final Path directory;
+ private final WatchHandler handler;
+
+ private WatchContext(final Path directory, final WatchHandler handler) {
+ this.directory = directory;
+ this.handler = handler;
+ }
+
+ static WatchContext of(final Path directory, final WatchHandler handler) {
+ return new WatchContext(directory, handler);
+ }
+
+ Path directory() {
+ return directory;
+ }
+
+ WatchHandler handler() {
+ return handler;
+ }
+
+ // TODO (jrp) should this just go away?
+ WatchHandler.Result handle(final WatchEvent event, final Path file) throws MojoExecutionException, IOException {
+ return handler.handle(this, event, directory.resolve(file));
+ }
+
+ @Override
+ public int compareTo(final WatchContext o) {
+ return directory().compareTo(o.directory());
+ }
+}
diff --git a/plugin/src/main/java/org/wildfly/plugin/dev/WatchHandler.java b/plugin/src/main/java/org/wildfly/plugin/dev/WatchHandler.java
new file mode 100644
index 00000000..8ecd58ce
--- /dev/null
+++ b/plugin/src/main/java/org/wildfly/plugin/dev/WatchHandler.java
@@ -0,0 +1,79 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2023 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.wildfly.plugin.dev;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+
+import org.apache.maven.plugin.MojoExecutionException;
+
+/**
+ * A handler for changes that happen to source files.
+ *
+ * @author James R. Perkins
+ */
+public interface WatchHandler {
+
+
+ Result handle(WatchContext context, WatchEvent event, Path file) throws IOException, MojoExecutionException;
+
+ /**
+ * The result of handling the changed source file.
+ */
+ interface Result {
+
+ /**
+ * Indicates whether a recompile is required.
+ *
+ * @return {@code true} if a recompile is required
+ */
+ default boolean requiresRecompile() {
+ return false;
+ }
+
+ /**
+ * Indicates whether the deployment should be redeployed is required.
+ *
+ * @return {@code true} if the deployment should be redeployed is required
+ */
+ default boolean requiresRedeploy() {
+ return false;
+ }
+
+ /**
+ * Indicates whether the resources should be copied.
+ *
+ * @return {@code true} if the resources should be copied
+ */
+ default boolean requiresCopyResources() {
+ return false;
+ }
+
+ /**
+ * Indicates whether the deployment should be repackaged.
+ *
+ * @return {@code true} if the deployment should be repackaged
+ */
+ default boolean requiresRepackage() {
+ return false;
+ }
+ }
+}
diff --git a/plugin/src/main/java/org/wildfly/plugin/dev/WebAppResourceHandler.java b/plugin/src/main/java/org/wildfly/plugin/dev/WebAppResourceHandler.java
new file mode 100644
index 00000000..0eba356d
--- /dev/null
+++ b/plugin/src/main/java/org/wildfly/plugin/dev/WebAppResourceHandler.java
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2023 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.wildfly.plugin.dev;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author James R. Perkins
+ */
+class WebAppResourceHandler implements WatchHandler {
+ private static final Set NO_DEPLOYMENT_WEB_FILE_EXTENSIONS = Set.of(
+ ".xhtml",
+ ".html",
+ ".jsp",
+ ".js",
+ ".css"
+ );
+
+ private final Set ignoredFileExtensions;
+
+ WebAppResourceHandler(final Collection ignoredFileExtensions) {
+ this.ignoredFileExtensions = ignoredFileExtensions.stream()
+ .map((value) -> value.charAt(0) == '.' ? value : "." + value)
+ .collect(Collectors.toCollection(HashSet::new));
+ this.ignoredFileExtensions.addAll(NO_DEPLOYMENT_WEB_FILE_EXTENSIONS);
+ }
+
+ @Override
+ public Result handle(final WatchContext context, final WatchEvent event, final Path file) throws IOException {
+ // Check the file extension to see if a redeploy should be ignored
+ final String fileName = file.getFileName().toString();
+ final int dot = fileName.lastIndexOf('.');
+ final boolean requiresRedeploy = dot <= 0 ||
+ !ignoredFileExtensions.contains(fileName.substring(dot).toLowerCase(Locale.ROOT));
+ return new Result() {
+
+ @Override
+ public boolean requiresRepackage() {
+ return true;
+ }
+
+ @Override
+ public boolean requiresRedeploy() {
+ return requiresRedeploy;
+ }
+ };
+ }
+}
diff --git a/plugin/src/main/java/org/wildfly/plugin/server/AbstractServerStartMojo.java b/plugin/src/main/java/org/wildfly/plugin/server/AbstractServerStartMojo.java
new file mode 100644
index 00000000..147b6bb2
--- /dev/null
+++ b/plugin/src/main/java/org/wildfly/plugin/server/AbstractServerStartMojo.java
@@ -0,0 +1,415 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2023 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.wildfly.plugin.server;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.jboss.as.controller.client.ModelControllerClient;
+import org.jboss.as.controller.client.helpers.domain.DomainClient;
+import org.jboss.galleon.ProvisioningException;
+import org.jboss.galleon.maven.plugin.util.MavenArtifactRepositoryManager;
+import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
+import org.wildfly.core.launcher.CommandBuilder;
+import org.wildfly.core.launcher.DomainCommandBuilder;
+import org.wildfly.core.launcher.Launcher;
+import org.wildfly.core.launcher.StandaloneCommandBuilder;
+import org.wildfly.plugin.common.AbstractServerConnection;
+import org.wildfly.plugin.common.Environment;
+import org.wildfly.plugin.common.PropertyNames;
+import org.wildfly.plugin.common.StandardOutput;
+import org.wildfly.plugin.common.Utils;
+import org.wildfly.plugin.core.GalleonUtils;
+import org.wildfly.plugin.core.MavenRepositoriesEnricher;
+import org.wildfly.plugin.core.ServerHelper;
+
+/**
+ * @author James R. Perkins
+ */
+public abstract class AbstractServerStartMojo extends AbstractServerConnection {
+
+ @Component
+ protected RepositorySystem repoSystem;
+
+ @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, required = true)
+ protected RepositorySystemSession session;
+
+ @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
+ protected List repositories;
+
+ @Parameter(defaultValue = "${project}", readonly = true, required = true)
+ protected MavenProject project;
+
+ @Parameter(defaultValue = "${session}", readonly = true, required = true)
+ protected MavenSession mavenSession;
+ /**
+ * The target directory the application to be deployed is located.
+ */
+ @Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true)
+ private File targetDir;
+
+ /**
+ * The WildFly Application Server's home directory. If not used, WildFly will be downloaded.
+ */
+ @Parameter(alias = "jboss-home", property = PropertyNames.JBOSS_HOME)
+ private String jbossHome;
+
+ /**
+ * The feature pack location. See the documentation
+ * for details on how to format a feature pack location.
+ *
+ * Note that if you define the version in the feature pack location, e.g. {@code #26.1.1.Final}, the {@code version}
+ * configuration parameter should be left blank.
+ *
+ */
+ @Parameter(alias = "feature-pack-location", property = PropertyNames.WILDFLY_FEATURE_PACK_LOCATION)
+ private String featurePackLocation;
+
+ /**
+ * The version of the WildFly default server to install in case no jboss-home has been set
+ * and no server has previously been provisioned.
+ *
+ * The latest stable version is resolved if left blank.
+ *
+ */
+ @Parameter(property = PropertyNames.WILDFLY_VERSION)
+ private String version;
+
+ /**
+ * The directory name inside the buildDir where to provision the default server.
+ * By default the server is provisioned into the 'server' directory.
+ *
+ * @since 3.0
+ */
+ @Parameter(alias = "provisioning-dir", property = PropertyNames.WILDFLY_PROVISIONING_DIR, defaultValue = Utils.WILDFLY_DEFAULT_DIR)
+ private String provisioningDir;
+
+ /**
+ * The modules path or paths to use. A single path can be used or multiple paths by enclosing them in a paths
+ * element.
+ */
+ @Parameter(alias = "modules-path", property = PropertyNames.MODULES_PATH)
+ private ModulesPath modulesPath;
+
+ /**
+ * The JVM options to use.
+ */
+ @Parameter(alias = "java-opts", property = PropertyNames.JAVA_OPTS)
+ private String[] javaOpts;
+
+ /**
+ * The {@code JAVA_HOME} to use for launching the server.
+ */
+ @Parameter(alias = "java-home", property = PropertyNames.JAVA_HOME)
+ private String javaHome;
+
+ /**
+ * Starts the server with debugging enabled.
+ */
+ @Parameter(property = "wildfly.debug", defaultValue = "false")
+ private boolean debug;
+
+ /**
+ * Sets the hostname to listen on for debugging. An {@code *} means all hosts.
+ */
+ @Parameter(property = "wildfly.debug.host", defaultValue = "*")
+ private String debugHost;
+
+ /**
+ * Sets the port the debugger should listen on.
+ */
+ @Parameter(property = "wildfly.debug.port", defaultValue = "8787")
+ private int debugPort;
+
+ /**
+ * Indicates whether the server should suspend itself until a debugger is attached.
+ */
+ @Parameter(property = "wildfly.debug.suspend", defaultValue = "false")
+ private boolean debugSuspend;
+
+ /**
+ * The path to the system properties file to load.
+ */
+ @Parameter(alias = "properties-file", property = PropertyNames.PROPERTIES_FILE)
+ private String propertiesFile;
+
+ /**
+ * The timeout value to use when starting the server.
+ */
+ @Parameter(alias = "startup-timeout", defaultValue = "60", property = PropertyNames.STARTUP_TIMEOUT)
+ private long startupTimeout;
+
+ /**
+ * The arguments to be passed to the server.
+ */
+ @Parameter(alias = "server-args", property = PropertyNames.SERVER_ARGS)
+ private String[] serverArgs;
+
+ /**
+ * Set to {@code true} if you want to skip this goal, otherwise {@code false}.
+ */
+ @Parameter(defaultValue = "false", property = PropertyNames.SKIP)
+ protected boolean skip;
+
+ /**
+ * The users to add to the server.
+ */
+ @Parameter(alias = "add-user", property = "wildfly.add-user")
+ private AddUser addUser;
+
+ /**
+ * Specifies the environment variables to be passed to the process being started.
+ *
+ *
+ * <env>
+ * <HOME>/home/wildfly/</HOME>
+ * </env>
+ *
+ *
+ */
+ @Parameter
+ private Map env;
+
+ protected MavenRepoManager mavenRepoManager;
+
+ protected ServerContext startServer(final ServerType serverType) throws MojoExecutionException, MojoFailureException {
+ final Log log = getLog();
+ MavenRepositoriesEnricher.enrich(mavenSession, project, repositories);
+ mavenRepoManager = new MavenArtifactRepositoryManager(repoSystem, session, repositories);
+
+ // Validate the environment
+ final Path jbossHome = provisionIfRequired(targetDir.toPath().resolve(provisioningDir));
+ if (!ServerHelper.isValidHomeDirectory(jbossHome)) {
+ throw new MojoExecutionException(String.format("JBOSS_HOME '%s' is not a valid directory.", jbossHome));
+ }
+
+ // Determine how stdout should be consumed
+ try {
+ final StandardOutput out = standardOutput();
+ // Create the server and close the client after the start. The process will continue running even after
+ // the maven process may have been finished
+ try (ModelControllerClient client = createClient()) {
+ if (ServerHelper.isStandaloneRunning(client) || ServerHelper.isDomainRunning(client)) {
+ throw new MojoExecutionException(String.format("%s server is already running?", serverType));
+ }
+ final CommandBuilder commandBuilder = createCommandBuilder(jbossHome);
+ log.info(String.format("%s server is starting up.", serverType));
+ final Launcher launcher = Launcher.of(commandBuilder)
+ .setRedirectErrorStream(true);
+ if (env != null) {
+ launcher.addEnvironmentVariables(env);
+ }
+ out.getRedirect().ifPresent(launcher::redirectOutput);
+
+ final Process process = launcher.launch();
+ // Note that if this thread is started and no shutdown goal is executed this stop the stdout and stderr
+ // from being logged any longer. The user was warned in the documentation.
+ out.startConsumer(process);
+ if (serverType == ServerType.DOMAIN) {
+ ServerHelper.waitForDomain(process, DomainClient.Factory.create(client), startupTimeout);
+ } else {
+ ServerHelper.waitForStandalone(process, client, startupTimeout);
+ }
+ if (!process.isAlive()) {
+ throw new MojoExecutionException("The process has been terminated before the start goal has completed.");
+ }
+ return new ServerContext() {
+ @Override
+ public Process process() {
+ return process;
+ }
+
+ @Override
+ public CommandBuilder commandBuilder() {
+ return commandBuilder;
+ }
+
+ @Override
+ public Path jbossHome() {
+ return jbossHome;
+ }
+ };
+ }
+ } catch (MojoExecutionException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new MojoExecutionException("The server failed to start", e);
+ }
+ }
+
+ protected abstract CommandBuilder createCommandBuilder(final Path jbossHome) throws MojoExecutionException;
+
+ protected StandardOutput standardOutput() throws IOException {
+ return StandardOutput.parse(null, false);
+ }
+
+ /**
+ * Allows the {@link #javaOpts} to be set as a string. The string is assumed to be space delimited.
+ *
+ * @param value a spaced delimited value of JVM options
+ */
+ @SuppressWarnings("unused")
+ public void setJavaOpts(final String value) {
+ if (value != null) {
+ javaOpts = value.split("\\s+");
+ }
+ }
+
+ protected StandaloneCommandBuilder createStandaloneCommandBuilder(final Path jbossHome, final String serverConfig) throws MojoExecutionException {
+ final StandaloneCommandBuilder commandBuilder = StandaloneCommandBuilder.of(jbossHome)
+ .setJavaHome(javaHome)
+ .addModuleDirs(modulesPath.getModulePaths());
+
+ // Set the JVM options
+ if (Utils.isNotNullOrEmpty(javaOpts)) {
+ commandBuilder.setJavaOptions(javaOpts);
+ }
+ if (debug) {
+ commandBuilder.addJavaOptions(String.format("-agentlib:jdwp=transport=dt_socket,server=y,suspend=%s,address=%s:%d",
+ debugSuspend ? "y" : "n", debugHost, debugPort));
+ }
+
+ if (serverConfig != null) {
+ commandBuilder.setServerConfiguration(serverConfig);
+ }
+
+ if (propertiesFile != null) {
+ commandBuilder.setPropertiesFile(propertiesFile);
+ }
+
+ if (serverArgs != null) {
+ commandBuilder.addServerArguments(serverArgs);
+ }
+
+ final Path javaHomePath = (this.javaHome == null ? Paths.get(System.getProperty("java.home")) : Paths.get(this.javaHome));
+ if (Environment.isModularJvm(javaHomePath)) {
+ commandBuilder.addJavaOptions(Environment.getModularJvmArguments());
+ }
+
+ // Print some server information
+ final Log log = getLog();
+ log.info("JAVA_HOME : " + commandBuilder.getJavaHome());
+ log.info("JBOSS_HOME: " + commandBuilder.getWildFlyHome());
+ log.info("JAVA_OPTS : " + Utils.toString(commandBuilder.getJavaOptions(), " "));
+ try {
+ addUsers(commandBuilder.getWildFlyHome(), commandBuilder.getJavaHome());
+ } catch (IOException e) {
+ throw new MojoExecutionException("Failed to add users", e);
+ }
+ return commandBuilder;
+ }
+
+ protected DomainCommandBuilder createDomainCommandBuilder(final Path jbossHome, final String domainConfig, final String hostConfig) throws MojoExecutionException {
+ final Path javaHome = (this.javaHome == null ? Paths.get(System.getProperty("java.home")) : Paths.get(this.javaHome));
+ final DomainCommandBuilder commandBuilder = DomainCommandBuilder.of(jbossHome, javaHome)
+ .addModuleDirs(modulesPath.getModulePaths());
+
+ // Set the JVM options
+ if (Utils.isNotNullOrEmpty(javaOpts)) {
+ commandBuilder.setProcessControllerJavaOptions(javaOpts)
+ .setHostControllerJavaOptions(javaOpts);
+ }
+
+ if (domainConfig != null) {
+ commandBuilder.setDomainConfiguration(domainConfig);
+ }
+
+ if (hostConfig != null) {
+ commandBuilder.setHostConfiguration(hostConfig);
+ }
+
+ if (propertiesFile != null) {
+ commandBuilder.setPropertiesFile(propertiesFile);
+ }
+
+ if (serverArgs != null) {
+ commandBuilder.addServerArguments(serverArgs);
+ }
+
+ // Workaround for WFCORE-4121
+ if (Environment.isModularJvm(javaHome)) {
+ commandBuilder.addHostControllerJavaOptions(Environment.getModularJvmArguments());
+ commandBuilder.addProcessControllerJavaOptions(Environment.getModularJvmArguments());
+ }
+
+ // Print some server information
+ final Log log = getLog();
+ log.info("JAVA_HOME : " + commandBuilder.getJavaHome());
+ log.info("JBOSS_HOME: " + commandBuilder.getWildFlyHome());
+ log.info("JAVA_OPTS : " + Utils.toString(commandBuilder.getHostControllerJavaOptions(), " "));
+ try {
+ addUsers(commandBuilder.getWildFlyHome(), commandBuilder.getJavaHome());
+ } catch (IOException e) {
+ throw new MojoExecutionException("Failed to add users", e);
+ }
+ return commandBuilder;
+ }
+
+ private Path provisionIfRequired(final Path installDir) throws MojoFailureException {
+ if (jbossHome != null) {
+ //we do not need to download WildFly
+ return Paths.get(jbossHome);
+ }
+ try {
+ if (!Files.exists(installDir)) {
+ getLog().info("Provisioning default server in " + installDir);
+ GalleonUtils.provision(installDir, resolveFeaturePackLocation(), version, mavenRepoManager);
+ }
+ return installDir;
+ } catch (ProvisioningException ex) {
+ throw new MojoFailureException(ex.getLocalizedMessage(), ex);
+ }
+ }
+
+ private void addUsers(final Path wildflyHome, final Path javaHome) throws IOException {
+ if (addUser != null && addUser.hasUsers()) {
+ getLog().info("Adding users: " + addUser);
+ addUser.addUsers(wildflyHome, javaHome);
+ }
+ }
+
+ private String resolveFeaturePackLocation() {
+ return featurePackLocation == null ? getDefaultFeaturePackLocation() : featurePackLocation;
+ }
+
+ /**
+ * Returns the default feature pack location if not defined in the configuration.
+ *
+ * @return the default feature pack location
+ */
+ protected String getDefaultFeaturePackLocation() {
+ return "wildfly@maven(org.jboss.universe:community-universe)";
+ }
+}
diff --git a/plugin/src/main/java/org/wildfly/plugin/server/RunMojo.java b/plugin/src/main/java/org/wildfly/plugin/server/RunMojo.java
index c557473f..0d0f11eb 100644
--- a/plugin/src/main/java/org/wildfly/plugin/server/RunMojo.java
+++ b/plugin/src/main/java/org/wildfly/plugin/server/RunMojo.java
@@ -23,48 +23,28 @@
package org.wildfly.plugin.server;
import java.io.File;
-import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import javax.inject.Inject;
-import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
-import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Execute;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
-import org.apache.maven.project.MavenProject;
-import org.eclipse.aether.RepositorySystem;
-import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.repository.RemoteRepository;
import org.jboss.as.controller.client.ModelControllerClient;
-import org.jboss.galleon.ProvisioningException;
-import org.jboss.galleon.maven.plugin.util.MavenArtifactRepositoryManager;
-import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
import org.wildfly.core.launcher.CommandBuilder;
-import org.wildfly.core.launcher.Launcher;
-import org.wildfly.core.launcher.StandaloneCommandBuilder;
import org.wildfly.plugin.cli.CommandConfiguration;
import org.wildfly.plugin.cli.CommandExecutor;
-import org.wildfly.plugin.common.AbstractServerConnection;
import org.wildfly.plugin.common.PropertyNames;
-import org.wildfly.plugin.common.Utils;
import org.wildfly.plugin.core.Deployment;
import org.wildfly.plugin.core.DeploymentManager;
-import org.wildfly.plugin.core.GalleonUtils;
-import org.wildfly.plugin.core.MavenRepositoriesEnricher;
-import org.wildfly.plugin.core.ServerHelper;
import org.wildfly.plugin.deployment.PackageType;
/**
@@ -77,79 +57,11 @@
*/
@Mojo(name = "run", requiresDependencyResolution = ResolutionScope.RUNTIME)
@Execute(phase = LifecyclePhase.PACKAGE)
-public class RunMojo extends AbstractServerConnection {
-
- @Component
- RepositorySystem repoSystem;
-
- @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, required = true)
- private RepositorySystemSession session;
-
- @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
- private List repositories;
-
- @Parameter(defaultValue = "${project}", readonly = true, required = true)
- MavenProject project;
-
- @Parameter(defaultValue = "${session}", readonly = true, required = true)
- MavenSession mavenSession;
+public class RunMojo extends AbstractServerStartMojo {
@Inject
private CommandExecutor commandExecutor;
- /**
- * The WildFly Application Server's home directory. If not used, WildFly will be installed.
- */
- @Parameter(alias = "jboss-home", property = PropertyNames.JBOSS_HOME)
- private String jbossHome;
-
- /**
- * The feature pack location. See the documentation
- * for details on how to format a feature pack location.
- *
- * Note that if you define the version in the feature pack location, e.g. {@code #26.1.1.Final}, the {@code version}
- * configuration parameter should be left blank.
- *
- */
- @Parameter(alias = "feature-pack-location", property = PropertyNames.WILDFLY_FEATURE_PACK_LOCATION)
- private String featurePackLocation;
-
- /**
- * The version of the WildFly default server to install in case no jboss-home has been set
- * and no server has previously been provisioned.
- * The latest stable version is resolved if left blank.
- */
- @Parameter(property = PropertyNames.WILDFLY_VERSION)
- private String version;
-
- /**
- * The directory name inside the buildDir where to provision the default server.
- * By default the server is provisioned into the 'server' directory.
- *
- * @since 3.0
- */
- @Parameter(alias = "provisioning-dir", property = PropertyNames.WILDFLY_PROVISIONING_DIR, defaultValue = Utils.WILDFLY_DEFAULT_DIR)
- private String provisioningDir;
-
- /**
- * The modules path or paths to use. A single path can be used or multiple paths by enclosing them in a paths
- * element.
- */
- @Parameter(alias = "modules-path", property = PropertyNames.MODULES_PATH)
- private ModulesPath modulesPath;
-
- /**
- * The JVM options to use.
- */
- @Parameter(alias = "java-opts", property = PropertyNames.JAVA_OPTS)
- private String[] javaOpts;
-
- /**
- * The {@code JAVA_HOME} to use for launching the server.
- */
- @Parameter(alias = "java-home", property = PropertyNames.JAVA_HOME)
- private String javaHome;
-
/**
* The CLI commands to execute before the deployment is deployed.
*/
@@ -168,43 +80,6 @@ public class RunMojo extends AbstractServerConnection {
@Parameter(alias = "server-config", property = PropertyNames.SERVER_CONFIG)
private String serverConfig;
- /**
- * The path to the system properties file to load.
- */
- @Parameter(alias = "properties-file", property = PropertyNames.PROPERTIES_FILE)
- private String propertiesFile;
-
- /**
- * The timeout value to use when starting the server.
- */
- @Parameter(alias = "startup-timeout", defaultValue = "60", property = PropertyNames.STARTUP_TIMEOUT)
- private long startupTimeout;
-
- /**
- * The arguments to be passed to the server.
- */
- @Parameter(alias = "server-args", property = PropertyNames.SERVER_ARGS)
- private String[] serverArgs;
-
- /**
- * The users to add to the server.
- */
- @Parameter(alias = "add-user", property = "wildfly.add-user")
- private AddUser addUser;
-
- /**
- * Specifies the environment variables to be passed to the process being started.
- *
- *
- * <env>
- * <HOME>/home/wildfly/</HOME>
- * </env>
- *
- *
- */
- @Parameter
- private Map env;
-
/**
* Specifies the name used for the deployment.
*/
@@ -240,52 +115,28 @@ public class RunMojo extends AbstractServerConnection {
@Parameter(property = PropertyNames.DEPLOYMENT_FILENAME)
private String filename;
- /**
- * Set to {@code true} if you want the deployment to be skipped, otherwise {@code false}.
- */
- @Parameter(defaultValue = "false", property = PropertyNames.SKIP)
- private boolean skip;
-
- private MavenRepoManager mavenRepoManager;
-
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
return;
}
- MavenRepositoriesEnricher.enrich(mavenSession, project, repositories);
- mavenRepoManager = new MavenArtifactRepositoryManager(repoSystem, session, repositories);
- final Log log = getLog();
- final Path deploymentContent = getDeploymentContent();
- // The deployment must exist before we do anything
- if (Files.notExists(deploymentContent)) {
- throw new MojoExecutionException(String.format("The deployment '%s' could not be found.", deploymentContent.toAbsolutePath()));
- }
- // Validate the environment
- final Path wildflyPath = provisionIfRequired(deploymentContent.getParent().resolve(provisioningDir));
- if (!ServerHelper.isValidHomeDirectory(wildflyPath)) {
- throw new MojoExecutionException(String.format("JBOSS_HOME '%s' is not a valid directory.", wildflyPath));
- }
- final StandaloneCommandBuilder commandBuilder = createCommandBuilder(wildflyPath);
-
- // Print some server information
- log.info("JAVA_HOME : " + commandBuilder.getJavaHome());
- log.info("JBOSS_HOME: " + commandBuilder.getWildFlyHome());
- log.info("JAVA_OPTS : " + Utils.toString(commandBuilder.getJavaOptions(), " "));
try {
- if (addUser != null && addUser.hasUsers()) {
- log.info("Adding users: " + addUser);
- addUser.addUsers(commandBuilder.getWildFlyHome(), commandBuilder.getJavaHome());
+ final Log log = getLog();
+ final Path deploymentContent = getDeploymentContent();
+ // The deployment must exist before we do anything
+ if (Files.notExists(deploymentContent)) {
+ throw new MojoExecutionException(String.format("The deployment '%s' could not be found.", deploymentContent.toAbsolutePath()));
}
// Start the server
log.info("Server is starting up. Press CTRL + C to stop the server.");
- Process process = startContainer(commandBuilder);
+ final ServerContext context = startServer(ServerType.STANDALONE);
+ Process process = context.process();
try (ModelControllerClient client = createClient()) {
// Execute commands before the deployment is done
final CommandConfiguration cmdConfig = CommandConfiguration.of(this::createClient, this::getClientConfiguration)
.addCommands(commands)
.addScripts(scripts)
- .setJBossHome(commandBuilder.getWildFlyHome())
+ .setJBossHome(context.jbossHome())
.setFork(true)
.setStdout("none")
.setTimeout(timeout)
@@ -312,7 +163,7 @@ public void execute() throws MojoExecutionException, MojoFailureException {
if (exitCode == 10) {
// Ensure the current process is destroyed and restart a new one
process.destroy();
- process = startContainer(commandBuilder);
+ process = startServer(ServerType.STANDALONE).process();
} else {
keepRunning = false;
}
@@ -327,56 +178,9 @@ public void execute() throws MojoExecutionException, MojoFailureException {
}
}
- /**
- * Allows the {@link #javaOpts} to be set as a string. The string is assumed to be space delimited.
- *
- * @param value a spaced delimited value of JVM options
- */
- @SuppressWarnings("unused")
- public void setJavaOpts(final String value) {
- if (value != null) {
- javaOpts = value.split("\\s+");
- }
- }
-
- private StandaloneCommandBuilder createCommandBuilder(final Path wildflyPath) {
- final StandaloneCommandBuilder commandBuilder = StandaloneCommandBuilder.of(wildflyPath)
- .setJavaHome(javaHome)
- .addModuleDirs(modulesPath.getModulePaths());
-
- // Set the JVM options
- if (Utils.isNotNullOrEmpty(javaOpts)) {
- commandBuilder.setJavaOptions(javaOpts);
- }
-
- if (serverConfig != null) {
- commandBuilder.setServerConfiguration(serverConfig);
- }
-
- if (propertiesFile != null) {
- commandBuilder.setPropertiesFile(propertiesFile);
- }
-
- if (serverArgs != null) {
- commandBuilder.addServerArguments(serverArgs);
- }
- return commandBuilder;
- }
-
- private Path provisionIfRequired(final Path installDir) throws MojoFailureException {
- if (jbossHome != null) {
- //we do not need to download WildFly
- return Paths.get(jbossHome);
- }
- try {
- if (!Files.exists(installDir)) {
- getLog().info("Provisioning default server in " + installDir);
- GalleonUtils.provision(installDir, resolveFeaturePackLocation(), version, mavenRepoManager);
- }
- return installDir;
- } catch (ProvisioningException ex) {
- throw new MojoFailureException(ex.getLocalizedMessage(), ex);
- }
+ @Override
+ protected CommandBuilder createCommandBuilder(final Path jbossHome) throws MojoExecutionException {
+ return createStandaloneCommandBuilder(jbossHome, serverConfig);
}
@Override
@@ -384,32 +188,6 @@ public String goal() {
return "run";
}
- private String resolveFeaturePackLocation() {
- return featurePackLocation == null ? getDefaultFeaturePackLocation() : featurePackLocation;
- }
-
- /**
- * Returns the default feature pack location if not defined in the configuration.
- *
- * @return the default feature pack location
- */
- protected String getDefaultFeaturePackLocation() {
- return "wildfly@maven(org.jboss.universe:community-universe)";
- }
-
- private Process startContainer(final CommandBuilder commandBuilder) throws IOException, InterruptedException, TimeoutException {
- final Launcher launcher = Launcher.of(commandBuilder)
- .inherit();
- if (env != null) {
- launcher.addEnvironmentVariables(env);
- }
- final Process process = launcher.launch();
- try (ModelControllerClient client = createClient()) {
- ServerHelper.waitForStandalone(process, client, startupTimeout);
- }
- return process;
- }
-
private Path getDeploymentContent() {
final PackageType packageType = PackageType.resolve(project);
final String filename;
diff --git a/plugin/src/main/java/org/wildfly/plugin/server/ServerContext.java b/plugin/src/main/java/org/wildfly/plugin/server/ServerContext.java
new file mode 100644
index 00000000..8fcb92e3
--- /dev/null
+++ b/plugin/src/main/java/org/wildfly/plugin/server/ServerContext.java
@@ -0,0 +1,53 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2023 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.wildfly.plugin.server;
+
+import java.nio.file.Path;
+
+import org.wildfly.core.launcher.CommandBuilder;
+
+/**
+ * The context of a server that has been started.
+ *
+ * @author James R. Perkins
+ */
+public interface ServerContext {
+
+ /**
+ * The running process.
+ *
+ * @return the process
+ */
+ Process process();
+
+ /**
+ * The command builder used to start the server.
+ *
+ * @return the command builder
+ */
+ CommandBuilder commandBuilder();
+
+ /**
+ * The directory used to start the server.
+ *
+ * @return the server directory
+ */
+ Path jbossHome();
+}
diff --git a/plugin/src/main/java/org/wildfly/plugin/server/StartMojo.java b/plugin/src/main/java/org/wildfly/plugin/server/StartMojo.java
index acc03d16..977a4355 100644
--- a/plugin/src/main/java/org/wildfly/plugin/server/StartMojo.java
+++ b/plugin/src/main/java/org/wildfly/plugin/server/StartMojo.java
@@ -22,43 +22,18 @@
package org.wildfly.plugin.server;
-import java.io.File;
import java.io.IOException;
-import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.Map;
-import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
-import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
-import org.apache.maven.project.MavenProject;
-import org.eclipse.aether.RepositorySystem;
-import org.eclipse.aether.RepositorySystemSession;
-import org.eclipse.aether.repository.RemoteRepository;
-import org.jboss.as.controller.client.ModelControllerClient;
-import org.jboss.as.controller.client.helpers.domain.DomainClient;
-import org.jboss.galleon.ProvisioningException;
-import org.jboss.galleon.maven.plugin.util.MavenArtifactRepositoryManager;
-import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
import org.wildfly.core.launcher.CommandBuilder;
-import org.wildfly.core.launcher.DomainCommandBuilder;
-import org.wildfly.core.launcher.Launcher;
-import org.wildfly.core.launcher.StandaloneCommandBuilder;
-import org.wildfly.plugin.common.AbstractServerConnection;
-import org.wildfly.plugin.common.Environment;
import org.wildfly.plugin.common.PropertyNames;
import org.wildfly.plugin.common.StandardOutput;
-import org.wildfly.plugin.common.Utils;
-import org.wildfly.plugin.core.GalleonUtils;
-import org.wildfly.plugin.core.MavenRepositoriesEnricher;
-import org.wildfly.plugin.core.ServerHelper;
/**
* Starts a standalone instance of WildFly Application Server.
@@ -68,80 +43,7 @@
* @author James R. Perkins
*/
@Mojo(name = "start", requiresDependencyResolution = ResolutionScope.RUNTIME)
-public class StartMojo extends AbstractServerConnection {
-
- @Component
- RepositorySystem repoSystem;
-
- @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, required = true)
- private RepositorySystemSession session;
-
- @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
- private List repositories;
-
- @Parameter(defaultValue = "${project}", readonly = true, required = true)
- MavenProject project;
-
- @Parameter(defaultValue = "${session}", readonly = true, required = true)
- MavenSession mavenSession;
- /**
- * The target directory the application to be deployed is located.
- */
- @Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true)
- private File targetDir;
-
- /**
- * The WildFly Application Server's home directory. If not used, WildFly will be downloaded.
- */
- @Parameter(alias = "jboss-home", property = PropertyNames.JBOSS_HOME)
- private String jbossHome;
-
- /**
- * The feature pack location. See the documentation
- * for details on how to format a feature pack location.
- *
- * Note that if you define the version in the feature pack location, e.g. {@code #26.1.1.Final}, the {@code version}
- * configuration parameter should be left blank.
- *
- */
- @Parameter(alias = "feature-pack-location", property = PropertyNames.WILDFLY_FEATURE_PACK_LOCATION)
- private String featurePackLocation;
-
- /**
- * The version of the WildFly default server to install in case no jboss-home has been set
- * and no server has previously been provisioned.
- * The latest stable version is resolved if left blank.
- */
- @Parameter(alias = "version", property = PropertyNames.WILDFLY_VERSION)
- private String version;
-
- /**
- * The directory name inside the buildDir where to provision the default server.
- * By default the server is provisioned into the 'server' directory.
- *
- * @since 3.0
- */
- @Parameter(alias = "provisioning-dir", property = PropertyNames.WILDFLY_PROVISIONING_DIR, defaultValue = Utils.WILDFLY_DEFAULT_DIR)
- private String provisioningDir;
-
- /**
- * The modules path or paths to use. A single path can be used or multiple paths by enclosing them in a paths
- * element.
- */
- @Parameter(alias = "modules-path", property = PropertyNames.MODULES_PATH)
- private ModulesPath modulesPath;
-
- /**
- * The JVM options to use.
- */
- @Parameter(alias = "java-opts", property = PropertyNames.JAVA_OPTS)
- private String[] javaOpts;
-
- /**
- * The {@code JAVA_HOME} to use for launching the server.
- */
- @Parameter(alias = "java-home", property = PropertyNames.JAVA_HOME)
- private String javaHome;
+public class StartMojo extends AbstractServerStartMojo {
/**
* The path to the server configuration to use. This is only used for standalone servers.
@@ -162,28 +64,13 @@ public class StartMojo extends AbstractServerConnection {
private String hostConfig;
/**
- * The path to the system properties file to load.
- */
- @Parameter(alias = "properties-file", property = PropertyNames.PROPERTIES_FILE)
- private String propertiesFile;
-
- /**
- * The timeout value to use when starting the server.
- */
- @Parameter(alias = "startup-timeout", defaultValue = "60", property = PropertyNames.STARTUP_TIMEOUT)
- private long startupTimeout;
-
- /**
- * The arguments to be passed to the server.
- */
- @Parameter(alias = "server-args", property = PropertyNames.SERVER_ARGS)
- private String[] serverArgs;
-
- /**
- * Set to {@code true} if you want to skip server start, otherwise {@code false}.
+ * The type of server to start.
+ *
+ * {@code STANDALONE} for a standalone server and {@code DOMAIN} for a domain server.
+ *
*/
- @Parameter(defaultValue = "false", property = PropertyNames.SKIP)
- private boolean skip;
+ @Parameter(alias = "server-type", property = "wildfly.server.type", defaultValue = "STANDALONE")
+ protected ServerType serverType;
/**
* Indicates how {@code stdout} and {@code stderr} should be handled for the spawned server process. Note that
@@ -209,36 +96,6 @@ public class StartMojo extends AbstractServerConnection {
@Parameter(property = PropertyNames.STDOUT)
private String stdout;
- /**
- * The users to add to the server.
- */
- @Parameter(alias = "add-user", property = "wildfly.add-user")
- private AddUser addUser;
-
- /**
- * The type of server to start.
- *
- * {@code STANDALONE} for a standalone server and {@code DOMAIN} for a domain server.
- *
- */
- @Parameter(alias = "server-type", property = "wildfly.server.type", defaultValue = "STANDALONE")
- private ServerType serverType;
-
- private MavenRepoManager mavenRepoManager;
-
- /**
- * Specifies the environment variables to be passed to the process being started.
- *
- *
- * <env>
- * <HOME>/home/wildfly/</HOME>
- * </env>
- *
- *
- */
- @Parameter
- private Map env;
-
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
final Log log = getLog();
@@ -246,46 +103,10 @@ public void execute() throws MojoExecutionException, MojoFailureException {
log.debug("Skipping server start");
return;
}
- MavenRepositoriesEnricher.enrich(mavenSession, project, repositories);
- mavenRepoManager = new MavenArtifactRepositoryManager(repoSystem, session, repositories);
-
- // Validate the environment
- final Path jbossHome = provisionIfRequired(targetDir.toPath().resolve(provisioningDir));
- if (!ServerHelper.isValidHomeDirectory(jbossHome)) {
- throw new MojoExecutionException(String.format("JBOSS_HOME '%s' is not a valid directory.", jbossHome));
- }
// Determine how stdout should be consumed
try {
- final StandardOutput out = StandardOutput.parse(stdout, true);
- // Create the server and close the client after the start. The process will continue running even after
- // the maven process may have been finished
- try (ModelControllerClient client = createClient()) {
- if (ServerHelper.isStandaloneRunning(client) || ServerHelper.isDomainRunning(client)) {
- throw new MojoExecutionException(String.format("%s server is already running?", serverType));
- }
- final CommandBuilder commandBuilder = createCommandBuilder(jbossHome);
- log.info(String.format("%s server is starting up.", serverType));
- final Launcher launcher = Launcher.of(commandBuilder)
- .setRedirectErrorStream(true);
- if (env != null) {
- launcher.addEnvironmentVariables(env);
- }
- out.getRedirect().ifPresent(launcher::redirectOutput);
-
- final Process process = launcher.launch();
- // Note that if this thread is started and no shutdown goal is executed this stop the stdout and stderr
- // from being logged any longer. The user was warned in the documentation.
- out.startConsumer(process);
- if (serverType == ServerType.DOMAIN) {
- ServerHelper.waitForDomain(process, DomainClient.Factory.create(client), startupTimeout);
- } else {
- ServerHelper.waitForStandalone(process, client, startupTimeout);
- }
- if (!process.isAlive()) {
- throw new MojoExecutionException("The process has been terminated before the start goal has completed.");
- }
- }
+ startServer(serverType);
} catch (MojoExecutionException e) {
throw e;
} catch (Exception e) {
@@ -293,145 +114,17 @@ public void execute() throws MojoExecutionException, MojoFailureException {
}
}
- /**
- * Allows the {@link #javaOpts} to be set as a string. The string is assumed to be space delimited.
- *
- * @param value a spaced delimited value of JVM options
- */
- @SuppressWarnings("unused")
- public void setJavaOpts(final String value) {
- if (value != null) {
- javaOpts = value.split("\\s+");
- }
- }
-
- private CommandBuilder createCommandBuilder(final Path jbossHome) throws MojoExecutionException {
+ @Override
+ protected CommandBuilder createCommandBuilder(final Path jbossHome) throws MojoExecutionException {
if (serverType == ServerType.DOMAIN) {
- return createDomainCommandBuilder(jbossHome);
- }
- return createStandaloneCommandBuilder(jbossHome);
- }
-
- private CommandBuilder createStandaloneCommandBuilder(final Path jbossHome) throws MojoExecutionException {
- final StandaloneCommandBuilder commandBuilder = StandaloneCommandBuilder.of(jbossHome)
- .setJavaHome(javaHome)
- .addModuleDirs(modulesPath.getModulePaths());
-
- // Set the JVM options
- if (Utils.isNotNullOrEmpty(javaOpts)) {
- commandBuilder.setJavaOptions(javaOpts);
- }
-
- if (serverConfig != null) {
- commandBuilder.setServerConfiguration(serverConfig);
- }
-
- if (propertiesFile != null) {
- commandBuilder.setPropertiesFile(propertiesFile);
- }
-
- if (serverArgs != null) {
- commandBuilder.addServerArguments(serverArgs);
- }
-
- final Path javaHomePath = (this.javaHome == null ? Paths.get(System.getProperty("java.home")) : Paths.get(this.javaHome));
- if (Environment.isModularJvm(javaHomePath)) {
- commandBuilder.addJavaOptions(Environment.getModularJvmArguments());
- }
-
- // Print some server information
- final Log log = getLog();
- log.info("JAVA_HOME : " + commandBuilder.getJavaHome());
- log.info("JBOSS_HOME: " + commandBuilder.getWildFlyHome());
- log.info("JAVA_OPTS : " + Utils.toString(commandBuilder.getJavaOptions(), " "));
- try {
- addUsers(commandBuilder.getWildFlyHome(), commandBuilder.getJavaHome());
- } catch (IOException e) {
- throw new MojoExecutionException("Failed to add users", e);
- }
- return commandBuilder;
- }
-
- private CommandBuilder createDomainCommandBuilder(final Path jbossHome) throws MojoExecutionException {
- final Path javaHome = (this.javaHome == null ? Paths.get(System.getProperty("java.home")) : Paths.get(this.javaHome));
- final DomainCommandBuilder commandBuilder = DomainCommandBuilder.of(jbossHome, javaHome)
- .addModuleDirs(modulesPath.getModulePaths());
-
- // Set the JVM options
- if (Utils.isNotNullOrEmpty(javaOpts)) {
- commandBuilder.setProcessControllerJavaOptions(javaOpts)
- .setHostControllerJavaOptions(javaOpts);
- }
-
- if (domainConfig != null) {
- commandBuilder.setDomainConfiguration(domainConfig);
- }
-
- if (hostConfig != null) {
- commandBuilder.setHostConfiguration(hostConfig);
- }
-
- if (propertiesFile != null) {
- commandBuilder.setPropertiesFile(propertiesFile);
+ return createDomainCommandBuilder(jbossHome, domainConfig, hostConfig);
}
-
- if (serverArgs != null) {
- commandBuilder.addServerArguments(serverArgs);
- }
-
- // Workaround for WFCORE-4121
- if (Environment.isModularJvm(javaHome)) {
- commandBuilder.addHostControllerJavaOptions(Environment.getModularJvmArguments());
- commandBuilder.addProcessControllerJavaOptions(Environment.getModularJvmArguments());
- }
-
- // Print some server information
- final Log log = getLog();
- log.info("JAVA_HOME : " + commandBuilder.getJavaHome());
- log.info("JBOSS_HOME: " + commandBuilder.getWildFlyHome());
- log.info("JAVA_OPTS : " + Utils.toString(commandBuilder.getHostControllerJavaOptions(), " "));
- try {
- addUsers(commandBuilder.getWildFlyHome(), commandBuilder.getJavaHome());
- } catch (IOException e) {
- throw new MojoExecutionException("Failed to add users", e);
- }
- return commandBuilder;
- }
-
- private Path provisionIfRequired(final Path installDir) throws MojoFailureException {
- if (jbossHome != null) {
- //we do not need to download WildFly
- return Paths.get(jbossHome);
- }
- try {
- if (!Files.exists(installDir)) {
- getLog().info("Provisioning default server in " + installDir);
- GalleonUtils.provision(installDir, resolveFeaturePackLocation(), version, mavenRepoManager);
- }
- return installDir;
- } catch (ProvisioningException ex) {
- throw new MojoFailureException(ex.getLocalizedMessage(), ex);
- }
- }
-
- private void addUsers(final Path wildflyHome, final Path javaHome) throws IOException {
- if (addUser != null && addUser.hasUsers()) {
- getLog().info("Adding users: " + addUser);
- addUser.addUsers(wildflyHome, javaHome);
- }
- }
-
- private String resolveFeaturePackLocation() {
- return featurePackLocation == null ? getDefaultFeaturePackLocation() : featurePackLocation;
+ return createStandaloneCommandBuilder(jbossHome, serverConfig);
}
- /**
- * Returns the default feature pack location if not defined in the configuration.
- *
- * @return the default feature pack location
- */
- protected String getDefaultFeaturePackLocation() {
- return "wildfly@maven(org.jboss.universe:community-universe)";
+ @Override
+ protected StandardOutput standardOutput() throws IOException {
+ return StandardOutput.parse(stdout, true);
}
@Override
diff --git a/plugin/src/site/markdown/dev-example.md.vm b/plugin/src/site/markdown/dev-example.md.vm
new file mode 100644
index 00000000..33808fcd
--- /dev/null
+++ b/plugin/src/site/markdown/dev-example.md.vm
@@ -0,0 +1,141 @@
+# Dev Examples
+
+The dev goal allows you to run a local instance of ${appServerName} and watches the source directories for changes. If
+required your deployment will be recompiled and possibly redeployed. This allows for more rapid development. Do note
+that large deployments may take longer to deploy.
+
+#[[##]]# Run overriding the feature pack location
+
+The example below shows how to run a server overriding the feature pack location:
+
+```xml
+
+ ...
+
+ ...
+
+ ...
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+
+ wildfly-preview@maven(org.jboss.universe:community-universe)
+
+
+ ...
+
+ ...
+
+...
+
+```
+
+#[[##]]# Run ignoring redeployment if properties files are changed
+
+The example below shows how to ignore properties files from triggering a redeploy:
+
+```xml
+
+ ...
+
+ ...
+
+ ...
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+
+
+ .properties
+
+
+
+ ...
+
+ ...
+
+...
+
+```
+
+#[[##]]# Add a user before running the server
+
+The example below shows how to add a user before running the server
+
+```xml
+
+ ...
+
+ ...
+
+ ...
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+
+
+
+
+ admin
+ admin.1234
+
+
+ admin-user
+ user.1234
+
+ admin
+ user
+
+ true
+
+
+ default-user
+ user.1234
+
+ user
+
+ true
+
+
+
+
+
+ ...
+
+ ...
+
+...
+
+```
+
+#[[##]]# Enable debugging
+
+The example below shows how to run a server in dev mode with debugging enabled
+
+```xml
+
+ ...
+
+ ...
+
+ ...
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+
+ true
+ 5005
+ true
+
+
+ ...
+
+ ...
+
+...
+
+```
\ No newline at end of file
diff --git a/plugin/src/site/markdown/index.md.vm b/plugin/src/site/markdown/index.md.vm
index a40610a4..58cddb83 100644
--- a/plugin/src/site/markdown/index.md.vm
+++ b/plugin/src/site/markdown/index.md.vm
@@ -71,6 +71,8 @@ Of course, patches are welcome, too. Contributors can check out the project from
* [Execute Commands Example](./execute-commands-example.html)
+ * [Dev Example](./dev-example.html)
+
* [Run Example](./run-example.html)
* [Packaging the application and server Example](./package-example.html)
diff --git a/plugin/src/site/markdown/run-example.md.vm b/plugin/src/site/markdown/run-example.md.vm
index d75cea73..909c8959 100644
--- a/plugin/src/site/markdown/run-example.md.vm
+++ b/plugin/src/site/markdown/run-example.md.vm
@@ -187,9 +187,9 @@ The example below shows how to run a server with debugging enabled
${project.artifactId}
${project.version}
-
- -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005
-
+ true
+ 5005
+ true
...
diff --git a/plugin/src/site/markdown/usage.md.vm b/plugin/src/site/markdown/usage.md.vm
index 2d03e6a8..fa180061 100644
--- a/plugin/src/site/markdown/usage.md.vm
+++ b/plugin/src/site/markdown/usage.md.vm
@@ -10,7 +10,7 @@ The `${pluginPrefix}:add-resource` goal adds a resource to the running ${appServ
For example to add a resource you type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:add-resource
```
@@ -20,7 +20,7 @@ The `${pluginPrefix}:deploy` goal deploys the application to the running ${appSe
For example to deploy, or redeploy by default, you type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:deploy
```
@@ -31,7 +31,7 @@ any other goals by default.
For example to deploy, or redeploy by default, you type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:deploy-only
```
@@ -41,17 +41,29 @@ The `${pluginPrefix}:deploy-artifact` goal deploys an arbitrary artifact to the
For example to deploy the arbitrary artifact specified in you POM, you type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:deploy-artifact
```
+#[[##]]# The `${pluginPrefix}:dev` Goal
+
+The `${pluginPrefix}:dev` goal will start ${appServerName} and deploy your application. If the `jboss-home`
+property is not set, a server will be provisioned. The source directories will be watched for changes. If a change
+occurs, additional Maven goals may be executed before a possible redeploy of your deployment.
+
+For example to run your WAR in development mode, you enter the following on the command line:
+
+```
+mvn ${pluginPrefix}:dev
+```
+
#[[##]]# The `${pluginPrefix}:package` Goal
The `${pluginPrefix}:package` goal will provision a server, execute CLI commands and deploy your application.
To execute the package goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:package
```
@@ -61,7 +73,7 @@ The `${pluginPrefix}:provision` goal will provision a server.
To execute the provision goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:provision
```
@@ -72,7 +84,7 @@ deployed to the application server.
To execute the redeploy goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:redeploy
```
@@ -83,7 +95,7 @@ deployed to the application server. By default no other goals are invoked.
To execute the redeploy goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:redeploy-only
```
@@ -94,30 +106,30 @@ application has already been deployed.
To execute the undeploy goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:undeploy
```
#[[##]]# The `${pluginPrefix}:run` Goal
-The `${pluginPrefix}:run>>> goal will run ${appServerName} and deploy your application. If the <<<${pluginPrefix}.home`
-property is not set, the server will be downloaded.
+The `${pluginPrefix}:run` goal will run ${appServerName} and deploy your application. If the `jboss-home`
+property is not set, a server will be provisioned.
To execute the run goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:run
```
#[[##]]# The `${pluginPrefix}:start` Goal
-The `${pluginPrefix}:start` goal will start a ${appServerName}. If the `${pluginPrefix}.home` property is not set,
-the server will be downloaded. The server will continue to run until the shutdown goal is executed, a shutdown management
+The `${pluginPrefix}:start` goal will start a ${appServerName}. If the `jboss-home` property is not set,
+a server will be provisioned. The server will continue to run until the shutdown goal is executed, a shutdown management
operation has been issued or the process is killed.
To execute the start goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:start
```
@@ -127,7 +139,7 @@ The `${pluginPrefix}:shutdown` goal will shutdown a running ${appServerName}.
To execute the shutdown goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:shutdown
```
@@ -137,6 +149,6 @@ The `${pluginPrefix}:execute-commands` goal will execute commands, formatted lik
To execute the execute-commands goal type the following on the command line:
-```bash
+```
mvn ${pluginPrefix}:execute-commands
```
\ No newline at end of file
diff --git a/plugin/src/site/site.xml b/plugin/src/site/site.xml
index 868093eb..7cbedf17 100644
--- a/plugin/src/site/site.xml
+++ b/plugin/src/site/site.xml
@@ -59,6 +59,7 @@
+
diff --git a/pom.xml b/pom.xml
index 58f011fa..224d79ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,6 +107,7 @@
0.9.1
1.1.0
0.3.5
+ 2.3.1
2.3.1