MEECROWAVE-207 basic reload command in run mojo
diff --git a/integration-tests/beanvalidation/pom.xml b/integration-tests/beanvalidation/pom.xml
index 3d0c7a9..1c3323a 100644
--- a/integration-tests/beanvalidation/pom.xml
+++ b/integration-tests/beanvalidation/pom.xml
@@ -53,6 +53,19 @@
</dependency>
</dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.meecrowave</groupId>
+ <artifactId>meecrowave-maven-plugin</artifactId>
+ <version>${project.version}</version>
+ <configuration>
+ <scanningPackageIncludes>org.apache.meecrowave.beanvalidation</scanningPackageIncludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
<profiles>
<profile>
<id>java11</id>
diff --git a/meecrowave-doc/src/main/jbake/content/meecrowave-maven/index.adoc b/meecrowave-doc/src/main/jbake/content/meecrowave-maven/index.adoc
index f82b7b2..132f99b 100755
--- a/meecrowave-doc/src/main/jbake/content/meecrowave-maven/index.adoc
+++ b/meecrowave-doc/src/main/jbake/content/meecrowave-maven/index.adoc
@@ -24,6 +24,14 @@
include::../../../../../target/generated-doc/MavenConfiguration.adoc[]
+== Run
+
+`mvn meecrowave:run` enables you to start a server configured in your `pom.xml`.
+Once started, you have a few commands you can use to interact with the server:
+
+- `quit`/`exit`: shutdown properly the server.
+- `reload` (since 1.2.9): optionally relaunch a maven compilation - see configuration - and reload the web context.
+
== Bundling
[source]
diff --git a/meecrowave-maven-plugin/pom.xml b/meecrowave-maven-plugin/pom.xml
index 73dc2cd..afc7dc8 100644
--- a/meecrowave-maven-plugin/pom.xml
+++ b/meecrowave-maven-plugin/pom.xml
@@ -30,7 +30,7 @@
<packaging>maven-plugin</packaging>
<properties>
- <maven.version>3.3.9</maven.version>
+ <maven.version>3.6.0</maven.version>
<meecrowave.build.name>${project.groupId}.maven</meecrowave.build.name>
</properties>
diff --git a/meecrowave-maven-plugin/src/main/java/org/apache/meecrowave/maven/MeecrowaveRunMojo.java b/meecrowave-maven-plugin/src/main/java/org/apache/meecrowave/maven/MeecrowaveRunMojo.java
index 427e2ff..0de0a53 100644
--- a/meecrowave-maven-plugin/src/main/java/org/apache/meecrowave/maven/MeecrowaveRunMojo.java
+++ b/meecrowave-maven-plugin/src/main/java/org/apache/meecrowave/maven/MeecrowaveRunMojo.java
@@ -18,20 +18,14 @@
*/
package org.apache.meecrowave.maven;
-import org.apache.logging.log4j.LogManager;
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugins.annotations.Mojo;
-import org.apache.maven.plugins.annotations.Parameter;
-import org.apache.maven.project.MavenProject;
-import org.apache.meecrowave.Meecrowave;
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static java.util.Optional.ofNullable;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static org.apache.maven.plugins.annotations.ResolutionScope.RUNTIME_PLUS_SYSTEM;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-import javax.script.SimpleBindings;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
@@ -47,14 +41,25 @@
import java.util.Map;
import java.util.Optional;
import java.util.Scanner;
+import java.util.function.Supplier;
-import static java.util.Collections.singletonList;
-import static java.util.Collections.singletonMap;
-import static java.util.Optional.ofNullable;
-import static java.util.function.Function.identity;
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toMap;
-import static org.apache.maven.plugins.annotations.ResolutionScope.RUNTIME_PLUS_SYSTEM;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+
+import org.apache.catalina.Context;
+import org.apache.logging.log4j.LogManager;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.lifecycle.internal.LifecycleStarter;
+import org.apache.maven.plugin.AbstractMojo;
+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.project.MavenProject;
+import org.apache.meecrowave.Meecrowave;
+import org.apache.meecrowave.tomcat.ProvidedLoader;
@Mojo(name = "run", requiresDependencyResolution = RUNTIME_PLUS_SYSTEM)
public class MeecrowaveRunMojo extends AbstractMojo {
@@ -214,6 +219,9 @@
@Parameter(defaultValue = "${project.build.outputDirectory}")
private List<File> modules;
+ @Parameter(defaultValue = "${session}", readonly = true)
+ private MavenSession session;
+
@Parameter(defaultValue = "${project}", readonly = true, required = true)
private MavenProject project;
@@ -299,6 +307,9 @@
@Parameter(property = "meecrowave.jaxws-support", defaultValue = "true")
private boolean jaxwsSupportIfAvailable;
+ @Parameter(property = "meecrowave.reload-goals", defaultValue = "process-classes")
+ private List<String> reloadGoals; // todo: add watching on project.build.directory?
+
@Parameter(property = "meecrowave.default-ssl-hostconfig-name")
private String defaultSSLHostConfigName;
@@ -308,12 +319,17 @@
@Parameter(property = "meecrowave.session-cookie-config")
private String webSessionCookieConfig;
+ @Component
+ private LifecycleStarter lifecycleStarter;
+
@Override
- public void execute() throws MojoExecutionException, MojoFailureException {
+ public void execute() {
if (skip) {
getLog().warn("Mojo skipped");
return;
}
+ logConfigurationErrors();
+
final Map<String, String> originalSystemProps;
if (systemProperties != null) {
originalSystemProps = systemProperties.keySet().stream()
@@ -326,8 +342,8 @@
final Thread thread = Thread.currentThread();
final ClassLoader loader = thread.getContextClassLoader();
- final ClassLoader appLoader = createClassLoader(loader);
- thread.setContextClassLoader(appLoader);
+ final Supplier<ClassLoader> appLoaderSupplier = createClassLoader(loader);
+ thread.setContextClassLoader(appLoaderSupplier.get());
try {
final Meecrowave.Builder builder = getConfig();
try (final Meecrowave meecrowave = new Meecrowave(builder) {
@@ -344,24 +360,34 @@
jsContextCustomizer == null ?
null : ctx -> scriptCustomization(
singletonList(jsContextCustomizer), "js", singletonMap("context", ctx)));
- if (useClasspathDeployment) {
- meecrowave.deployClasspath(deploymentMeta);
- } else {
- meecrowave.deployWebapp(deploymentMeta);
+ deploy(meecrowave, deploymentMeta);
+ final Scanner scanner = new Scanner(System.in);
+ String cmd;
+ boolean quit = false;
+ while (!quit && (cmd = scanner.next()) != null) {
+ cmd = cmd.trim();
+ switch (cmd) {
+ case "": // normally impossible with a Scanner but we can move to another "reader"
+ case "q":
+ case "quit":
+ case "e":
+ case "exit":
+ quit = true;
+ break;
+ case "r":
+ case "reload":
+ reload(meecrowave, fixedContext, appLoaderSupplier, loader);
+ break;
+ default:
+ getLog().error("Unknown command: '" + cmd + "', use 'quit' or 'exit' or 'reload'");
+ }
}
- new Scanner(System.in).next();
}
} finally {
if (forceLog4j2Shutdown) {
LogManager.shutdown();
}
- if (appLoader != loader) {
- try {
- URLClassLoader.class.cast(appLoader).close();
- } catch (final IOException e) {
- getLog().warn(e.getMessage(), e);
- }
- }
+ destroyTcclIfNeeded(thread, loader);
thread.setContextClassLoader(loader);
if (originalSystemProps != null) {
systemProperties.keySet().forEach(k -> {
@@ -376,6 +402,51 @@
}
}
+ private void destroyTcclIfNeeded(final Thread thread, final ClassLoader loader) {
+ if (thread.getContextClassLoader() != loader) {
+ try {
+ URLClassLoader.class.cast(thread.getContextClassLoader()).close();
+ } catch (final IOException e) {
+ getLog().warn(e.getMessage(), e);
+ }
+ }
+ }
+
+ private void logConfigurationErrors() {
+ if (watcherBouncing > 0 && reloadGoals != null && !reloadGoals.isEmpty()) {
+ getLog().warn("You set reloadGoals and watcherBouncing > 1, behavior is undefined");
+ }
+ }
+
+ private void reload(final Meecrowave meecrowave, final String context,
+ final Supplier<ClassLoader> loaderSupplier, final ClassLoader mojoLoader) {
+ if (reloadGoals != null && !reloadGoals.isEmpty()) {
+ final List<String> goals = session.getGoals();
+ session.getRequest().setGoals(reloadGoals);
+ try {
+ lifecycleStarter.execute(session);
+ } finally {
+ session.getRequest().setGoals(goals);
+ }
+ }
+ final Context ctx = Context.class.cast(meecrowave.getTomcat().getHost().findChild(context));
+ if (useClasspathDeployment) {
+ final Thread thread = Thread.currentThread();
+ destroyTcclIfNeeded(thread, mojoLoader);
+ thread.setContextClassLoader(loaderSupplier.get());
+ ctx.setLoader(new ProvidedLoader(thread.getContextClassLoader(), meecrowave.getConfiguration().isTomcatWrapLoader()));
+ }
+ ctx.reload();
+ }
+
+ private void deploy(final Meecrowave meecrowave, final Meecrowave.DeploymentMeta deploymentMeta) {
+ if (useClasspathDeployment) {
+ meecrowave.deployClasspath(deploymentMeta);
+ } else {
+ meecrowave.deployWebapp(deploymentMeta);
+ }
+ }
+
private void scriptCustomization(final List<String> customizers, final String ext, final Map<String, Object> customBindings) {
if (customizers == null || customizers.isEmpty()) {
return;
@@ -396,7 +467,7 @@
}
}
- private ClassLoader createClassLoader(final ClassLoader parent) {
+ private Supplier<ClassLoader> createClassLoader(final ClassLoader parent) {
final List<URL> urls = new ArrayList<>();
urls.addAll(project.getArtifacts().stream()
.filter(a -> !((applicationScopes == null && !(Artifact.SCOPE_COMPILE.equals(a.getScope()) || Artifact.SCOPE_RUNTIME.equals(a.getScope())))
@@ -416,7 +487,7 @@
throw new IllegalArgumentException(e);
}
}).collect(toList()));
- return urls.isEmpty() ? parent : new URLClassLoader(urls.toArray(new URL[urls.size()]), parent) {
+ return urls.isEmpty() ? () -> parent : () -> new URLClassLoader(urls.toArray(new URL[0]), parent) {
@Override
public boolean equals(final Object obj) {
return super.equals(obj) || parent.equals(obj);
diff --git a/meecrowave-maven-plugin/src/test/java/org/apache/meecrowave/maven/MeecrowaveRunMojoTest.java b/meecrowave-maven-plugin/src/test/java/org/apache/meecrowave/maven/MeecrowaveRunMojoTest.java
index 730911f..84b2dc0 100644
--- a/meecrowave-maven-plugin/src/test/java/org/apache/meecrowave/maven/MeecrowaveRunMojoTest.java
+++ b/meecrowave-maven-plugin/src/test/java/org/apache/meecrowave/maven/MeecrowaveRunMojoTest.java
@@ -34,11 +34,13 @@
import org.junit.Rule;
import org.junit.Test;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -76,7 +78,7 @@
final InputStream in = System.in;
final CountDownLatch latch = new CountDownLatch(1);
System.setIn(new InputStream() {
- private int val = 2; // just to not return nothing
+ private final InputStream delegate = new ByteArrayInputStream("quit".getBytes(StandardCharsets.UTF_8));
@Override
public int read() throws IOException {
@@ -86,7 +88,7 @@
Thread.currentThread().interrupt();
fail(e.getMessage());
}
- return val--;
+ return delegate.read();
}
});
final Thread runner = new Thread() {