MEECROWAVE-290: Implement reloadCallback
diff --git a/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java b/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
index 705567f..4bb064d 100644
--- a/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
+++ b/meecrowave-core/src/main/java/org/apache/meecrowave/Meecrowave.java
@@ -190,7 +190,7 @@
return deployWebapp(new DeploymentMeta(meta.context, meta.docBase, ofNullable(meta.consumer).map(c -> (Consumer<Context>) ctx -> {
builtInCustomizer.accept(ctx);
c.accept(ctx);
- }).orElse(builtInCustomizer)));
+ }).orElse(builtInCustomizer), meta.redeployCallback));
}
// shortcut
@@ -201,12 +201,12 @@
// shortcut
public Meecrowave bake(final Consumer<Context> customizer) {
start();
- return deployClasspath(new DeploymentMeta("", null, customizer));
+ return deployClasspath(new DeploymentMeta("", null, customizer, null));
}
// shortcut (used by plugins)
public Meecrowave deployClasspath(final String context) {
- return deployClasspath(new DeploymentMeta(context, null, null));
+ return deployClasspath(new DeploymentMeta(context, null, null, null));
}
// shortcut
@@ -216,7 +216,7 @@
// shortcut (used by plugins)
public Meecrowave deployWebapp(final String context, final File warOrDir) {
- return deployWebapp(new DeploymentMeta(context, warOrDir, null));
+ return deployWebapp(new DeploymentMeta(context, warOrDir, null, null));
}
public Meecrowave deployWebapp(final DeploymentMeta meta) {
@@ -320,7 +320,7 @@
}
};
- ctx.addLifecycleListener(new MeecrowaveContextConfig(configuration, meta.docBase != null, meecrowaveInitializer));
+ ctx.addLifecycleListener(new MeecrowaveContextConfig(configuration, meta.docBase != null, meecrowaveInitializer, meta.redeployCallback));
ctx.addLifecycleListener(event -> {
switch (event.getType()) {
case Lifecycle.BEFORE_START_EVENT:
@@ -1973,14 +1973,16 @@
// there to be able to stack config later on without breaking all methods
public static class DeploymentMeta {
- private final String context;
+ private final String context;
private final File docBase;
private final Consumer<Context> consumer;
+ private final Consumer<Context> redeployCallback;
- public DeploymentMeta(final String context, final File docBase, final Consumer<Context> consumer) {
+ public DeploymentMeta(final String context, final File docBase, final Consumer<Context> consumer, final Consumer<Context> redeployCallback) {
this.context = context;
this.docBase = docBase;
this.consumer = consumer;
+ this.redeployCallback = redeployCallback;
}
}
diff --git a/meecrowave-core/src/main/java/org/apache/meecrowave/runner/Cli.java b/meecrowave-core/src/main/java/org/apache/meecrowave/runner/Cli.java
index e5545a7..220b6ac 100644
--- a/meecrowave-core/src/main/java/org/apache/meecrowave/runner/Cli.java
+++ b/meecrowave-core/src/main/java/org/apache/meecrowave/runner/Cli.java
@@ -91,6 +91,7 @@
.filter(File::isDirectory)
.findFirst()
.orElse(null)),
+ null,
null));
} else {
meecrowave.deployWebapp(fixedCtx, new File(war));
diff --git a/meecrowave-core/src/main/java/org/apache/meecrowave/tomcat/MeecrowaveContextConfig.java b/meecrowave-core/src/main/java/org/apache/meecrowave/tomcat/MeecrowaveContextConfig.java
index 261da27..1bd5124 100644
--- a/meecrowave-core/src/main/java/org/apache/meecrowave/tomcat/MeecrowaveContextConfig.java
+++ b/meecrowave-core/src/main/java/org/apache/meecrowave/tomcat/MeecrowaveContextConfig.java
@@ -34,6 +34,7 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.servlet.ServletContainerInitializer;
@@ -68,13 +69,15 @@
private final Map<String, Collection<Class<?>>> webClasses = new HashMap<>();
private final boolean fixDocBase;
private final ServletContainerInitializer intializer;
+ private final Consumer<Context> redeployCallback;
private OwbAnnotationFinder finder;
private ReloadOnChangeController watcher;
- public MeecrowaveContextConfig(final Configuration configuration, final boolean fixDocBase, final ServletContainerInitializer intializer) {
+ public MeecrowaveContextConfig(final Configuration configuration, final boolean fixDocBase, final ServletContainerInitializer intializer, final Consumer<Context> redeployCallback) {
this.configuration = configuration;
this.fixDocBase = fixDocBase;
this.intializer= intializer;
+ this.redeployCallback = redeployCallback;
}
@Override
@@ -111,7 +114,7 @@
scannerService.setDocBase(context.getDocBase());
scannerService.setShared(configuration.getSharedLibraries());
if (configuration.getWatcherBouncing() > 0) { // note that caching should be disabled with this config in most of the times
- watcher = new ReloadOnChangeController(context, configuration.getWatcherBouncing());
+ watcher = new ReloadOnChangeController(context, configuration.getWatcherBouncing(), redeployCallback);
scannerService.setFileVisitor(f -> watcher.register(f));
}
scannerService.scan();
diff --git a/meecrowave-core/src/main/java/org/apache/meecrowave/watching/ReloadOnChangeController.java b/meecrowave-core/src/main/java/org/apache/meecrowave/watching/ReloadOnChangeController.java
index 0a34601..a75ed04 100644
--- a/meecrowave-core/src/main/java/org/apache/meecrowave/watching/ReloadOnChangeController.java
+++ b/meecrowave-core/src/main/java/org/apache/meecrowave/watching/ReloadOnChangeController.java
@@ -18,8 +18,8 @@
*/
package org.apache.meecrowave.watching;
-import org.apache.catalina.Context;
-import org.apache.meecrowave.logging.tomcat.LogFacade;
+import static java.util.Arrays.asList;
+import static java.util.Optional.ofNullable;
import java.io.File;
import java.io.IOException;
@@ -36,12 +36,15 @@
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
-import static java.util.Arrays.asList;
+import org.apache.catalina.Context;
+import org.apache.meecrowave.logging.tomcat.LogFacade;
public class ReloadOnChangeController implements AutoCloseable, Runnable {
private final Context context;
private final long bouncing;
+ private final Consumer<Context> redeployCallback;
private final Collection<Path> paths = new ArrayList<>();
private WatchService watchService;
private Thread bouncer;
@@ -49,9 +52,10 @@
private volatile boolean running = true;
private volatile long redeployMarker = System.nanoTime();
- public ReloadOnChangeController(final Context context, final int watcherBouncing) {
+ public ReloadOnChangeController(final Context context, final int watcherBouncing, final Consumer<Context> redeployCallback) {
this.context = context;
this.bouncing = (long) watcherBouncing;
+ this.redeployCallback = ofNullable(redeployCallback).orElse(Context::reload);
}
public void register(final File folder) {
@@ -79,7 +83,7 @@
}
protected synchronized void redeploy() {
- context.reload();
+ redeployCallback.accept(context);
}
@Override
diff --git a/meecrowave-core/src/test/java/org/apache/meecrowave/MeecrowaveTest.java b/meecrowave-core/src/test/java/org/apache/meecrowave/MeecrowaveTest.java
index f7cd553..e5a2caa 100644
--- a/meecrowave-core/src/test/java/org/apache/meecrowave/MeecrowaveTest.java
+++ b/meecrowave-core/src/test/java/org/apache/meecrowave/MeecrowaveTest.java
@@ -215,6 +215,19 @@
assertEquals(
"sci:" + Bounced.class.getName() + Endpoint.class.getName() + InterfaceApi.class.getName() + RsApp.class.getName() + TestJsonEndpoint.class.getName(),
slurp(new URL("http://localhost:" + meecrowave.getConfiguration().getHttpPort() + "/sci")));
+ assertNotAvailable(new URL("http://localhost:" + meecrowave.getConfiguration().getHttpPort() + "/api/other"));
+ assertNotAvailable(new URL("http://localhost:" + meecrowave.getConfiguration().getHttpPort() + "/other"));
+ }
+
+ private void assertNotAvailable(final URL url) {
+ try {
+ URLConnection connection = url.openConnection();
+ connection.setReadTimeout(500);
+ connection.getInputStream();
+ fail(url.toString() + " is available");
+ } catch (Exception e) {
+ assertTrue(e.getMessage(), e instanceof IOException);
+ }
}
private String slurp(final URL url) {
diff --git a/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MeecrowaveRule.java b/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MeecrowaveRule.java
index ba30e23..40a47b1 100644
--- a/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MeecrowaveRule.java
+++ b/meecrowave-junit/src/main/java/org/apache/meecrowave/junit/MeecrowaveRule.java
@@ -58,7 +58,7 @@
protected AutoCloseable onStart() {
final Meecrowave meecrowave = new Meecrowave(configuration);
meecrowave.start();
- meecrowave.deployClasspath(new Meecrowave.DeploymentMeta(context, docBase, customizer));
+ meecrowave.deployClasspath(new Meecrowave.DeploymentMeta(context, docBase, customizer, null));
return meecrowave;
}
}
diff --git a/meecrowave-maven-plugin/pom.xml b/meecrowave-maven-plugin/pom.xml
index 6d99bf1..bb5b1a2 100644
--- a/meecrowave-maven-plugin/pom.xml
+++ b/meecrowave-maven-plugin/pom.xml
@@ -34,6 +34,19 @@
<meecrowave.build.name>${project.groupId}.maven</meecrowave.build.name>
</properties>
+ <profiles>
+ <profile>
+ <id>dev</id> <!-- IDE does not see that it is a shade so workaround it with an IDE profile -->
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.meecrowave</groupId>
+ <artifactId>meecrowave-specs-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+ </profile>
+ </profiles>
+
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
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 378c356..7f2bd04 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
@@ -309,7 +309,7 @@
private boolean jaxwsSupportIfAvailable;
@Parameter(property = "meecrowave.reload-goals")
- private List<String> reloadGoals; // todo: add watching on project.build.directory?
+ private List<String> reloadGoals;
@Parameter(property = "meecrowave.default-ssl-hostconfig-name")
private String defaultSSLHostConfigName;
@@ -367,7 +367,8 @@
webapp != null && webapp.isDirectory() ? webapp : null,
jsContextCustomizer == null ?
null : ctx -> scriptCustomization(
- singletonList(jsContextCustomizer), "js", singletonMap("context", ctx)));
+ singletonList(jsContextCustomizer), "js", singletonMap("context", ctx)),
+ context -> reload(meecrowave, fixedContext, appLoaderSupplier, loader));
deploy(meecrowave, deploymentMeta);
final Scanner scanner = new Scanner(System.in);
String cmd;
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 84b2dc0..f487538 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
@@ -18,7 +18,34 @@
*/
package org.apache.meecrowave.maven;
+import static java.util.Optional.ofNullable;
+import static org.apache.ziplock.JarLocation.jarLocation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import javax.enterprise.inject.Model;
+
import org.apache.commons.io.IOUtils;
+import org.apache.cxf.helpers.FileUtils;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
@@ -27,34 +54,54 @@
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingRequest;
-import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.apache.meecrowave.io.IO;
+import org.app.Endpoint;
+import org.app.Injectable;
+import org.app.RsApp;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
import org.eclipse.aether.repository.LocalRepository;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
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;
-
-import static org.apache.ziplock.JarLocation.jarLocation;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
public class MeecrowaveRunMojoTest {
- @Rule
+
+ private static final int RETRY_COUNT = 1000;
+ private static final int RETRY_WAIT_PERIOD = 500;
+
+ private static byte[] additionalEndpointClass;
+
+ @Rule
public final MojoRule mojo = new MojoRule();
- @Test
- public void run() throws Exception {
+ private MavenProject project;
+ private MavenSession session;
+ private int port;
+ private MojoExecution execution;
+
+ @BeforeClass
+ public static void removeAdditionalEndpointClass() throws Exception {
+ File additionalEndpointClassFile = getAdditionalEndpointClass();
+ try (InputStream classStream = new FileInputStream(additionalEndpointClassFile)) {
+ additionalEndpointClass = IOUtils.toByteArray(classStream);
+ }
+ assumeTrue(additionalEndpointClassFile.delete());
+ }
+
+ @AfterClass
+ public static void restoreAdditionalEndpointClass() throws Exception {
+ IOUtils.write(additionalEndpointClass, new FileOutputStream(getAdditionalEndpointClass()));
+ }
+
+ private static File getAdditionalEndpointClass() throws URISyntaxException {
+ return new File(new File(MeecrowaveRunMojoTest.class.getResource("/").toURI()), "org/app/AdditionalEndpoint.class");
+ }
+
+ @Before
+ public void setupMojoExecution() throws Exception {
final File moduleBase = jarLocation(MeecrowaveRunMojoTest.class).getParentFile().getParentFile();
final File basedir = new File(moduleBase, "src/test/resources/" + getClass().getSimpleName());
final File pom = new File(basedir, "pom.xml");
@@ -65,16 +112,120 @@
repositorySession.setLocalRepositoryManager(new SimpleLocalRepositoryManagerFactory()
.newInstance(repositorySession, new LocalRepository(new File(moduleBase, "target/fake"), "")));
configuration.setRepositorySession(repositorySession);
- final MavenProject project = mojo.lookup(ProjectBuilder.class).build(pom, configuration).getProject();
- final MavenSession session = mojo.newMavenSession(project);
- final int port;
+ project = mojo.lookup(ProjectBuilder.class).build(pom, configuration).getProject();
+ session = mojo.newMavenSession(project);
try (final ServerSocket serverSocket = new ServerSocket(0)) {
port = serverSocket.getLocalPort();
}
- final MojoExecution execution = mojo.newMojoExecution("run");
- execution.getConfiguration().addChild(new Xpp3Dom("httpPort") {{
- setValue(Integer.toString(port));
- }});
+ execution = mojo.newMojoExecution("run");
+ }
+
+ @Test
+ public void classpathDeployment() throws Exception {
+ execution.getConfiguration().getChild("httpPort").setValue(Integer.toString(port));
+ final Runnable quitCommand = quitCommand();
+ final Thread mojoExecutor = mojoExecutor();
+ try {
+ mojoExecutor.start();
+ retry(() -> {
+ assertEquals("simple", IOUtils.toString(new URL("http://localhost:" + port + "/api/test")));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("first_name"));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("last_name"));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("firstname"));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("null"));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/sub/index.html")).contains("<h1>yes</h1>"));
+ assertNotAvailable(new URL("http://localhost:" + port + "/api/additional"));
+ quitCommand.run();
+ });
+ } finally {
+ mojoExecutor.join(TimeUnit.MINUTES.toMillis(1));
+ if (mojoExecutor.isAlive()) {
+ mojoExecutor.interrupt();
+ fail("Runner didn't terminate properly");
+ }
+ }
+ }
+
+ @Test
+ public void webappDeployment() throws Exception {
+ File target = new File(MeecrowaveRunMojoTest.class.getResource("/").toURI()).getParentFile();
+ File webappDirectory = new File(target, MeecrowaveRunMojoTest.class.getSimpleName());
+ webappDirectory.mkdir();
+ assertTrue(webappDirectory.exists());
+ setupWebapp(webappDirectory);
+ execution.getConfiguration().getChild("httpPort").setValue(Integer.toString(port));
+ execution.getConfiguration().getChild("useClasspathDeployment").setValue("false");
+ execution.getConfiguration().getChild("webapp").setValue(webappDirectory.getAbsolutePath());
+ final Runnable quitCommand = quitCommand();
+ final Thread mojoExecutor = mojoExecutor();
+ try {
+ mojoExecutor.start();
+ retry(() -> {
+ assertEquals("simple", IOUtils.toString(new URL("http://localhost:" + port + "/api/test")));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("first_name"));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("last_name"));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("firstname"));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("null"));
+ assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/additional")).contains("available"));
+ assertNotAvailable(new URL("http://localhost:" + port + "/sub/index.html"));
+ quitCommand.run();
+ });
+ } finally {
+ mojoExecutor.join(TimeUnit.MINUTES.toMillis(1));
+ if (mojoExecutor.isAlive()) {
+ mojoExecutor.interrupt();
+ fail("Runner didn't terminate properly");
+ }
+ }
+ }
+
+ @Test
+ public void autoreloadWithClasspathDeployment() throws Exception {
+ File additionalEndpointFile = getAdditionalEndpointClass();
+ execution.getConfiguration().getChild("httpPort").setValue(Integer.toString(port));
+ execution.getConfiguration().getChild("watcherBouncing").setValue("1");
+ Runnable quitCommand = quitCommand();
+ final Thread mojoExecutor = mojoExecutor();
+ try {
+ mojoExecutor.start();
+ retry(() -> {
+ assertEquals("simple", IOUtils.toString(new URL("http://localhost:" + port + "/api/test")));
+ assertNotAvailable(new URL("http://localhost:" + port + "/api/additional"));
+ });
+ File folder = additionalEndpointFile.getParentFile();
+ folder.mkdirs();
+ assertTrue(folder.exists());
+ IOUtils.write(additionalEndpointClass, new FileOutputStream(additionalEndpointFile));
+ retry(() -> assertEquals("available", IOUtils.toString(new URL("http://localhost:" + port + "/api/additional"))));
+ retry(() -> assertEquals("simple", IOUtils.toString(new URL("http://localhost:" + port + "/api/test"))));
+ quitCommand.run();
+ } finally {
+ additionalEndpointFile.delete();
+ assertFalse(additionalEndpointFile.exists());
+ mojoExecutor.join(TimeUnit.MINUTES.toMillis(1));
+ if (mojoExecutor.isAlive()) {
+ mojoExecutor.interrupt();
+ fail("Runner didn't terminate properly");
+ }
+ }
+ }
+
+ private void setupWebapp(File webappDirectory) throws Exception {
+ Stream.of(Endpoint.class, RsApp.class, Injectable.class, Model.class).forEach(type -> {
+ final String target = type.getName().replace(".", "/");
+ File targetFile = new File(webappDirectory, "WEB-INF/classes/" + target + ".class");
+ FileUtils.mkDir(targetFile.getParentFile());
+ try (final InputStream from = Thread.currentThread().getContextClassLoader().getResourceAsStream(target + ".class");
+ final OutputStream to = new FileOutputStream(targetFile)) {
+ IO.copy(from, to);
+ } catch (final IOException e) {
+ fail(e.getMessage());
+ }
+ });
+ IOUtils.write(additionalEndpointClass, new FileOutputStream(new File(webappDirectory, "WEB-INF/classes/org/app/AdditionalEndpoint.class")));
+ }
+
+ private Runnable quitCommand() {
final InputStream in = System.in;
final CountDownLatch latch = new CountDownLatch(1);
System.setIn(new InputStream() {
@@ -88,10 +239,19 @@
Thread.currentThread().interrupt();
fail(e.getMessage());
}
- return delegate.read();
+ if (delegate.available() > 0) {
+ return delegate.read();
+ } else {
+ System.setIn(in);
+ return -1;
+ }
}
});
- final Thread runner = new Thread() {
+ return latch::countDown;
+ }
+
+ private Thread mojoExecutor() {
+ return new Thread() {
@Override
public void run() {
try {
@@ -101,29 +261,34 @@
}
}
};
+ }
+
+ private void assertNotAvailable(final URL url) {
try {
- runner.start();
- for (int i = 0; i < 120; i++) {
- try {
- assertEquals("simple", IOUtils.toString(new URL("http://localhost:" + port + "/api/test")));
- assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("first_name"));
- assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("last_name"));
- assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("firstname"));
- assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/api/test/model")).contains("null"));
- assertTrue(IOUtils.toString(new URL("http://localhost:" + port + "/sub/index.html")).contains("<h1>yes</h1>"));
- latch.countDown();
- break;
- } catch (final Exception | AssertionError e) {
- Thread.sleep(500);
- }
- }
- } finally {
- runner.join(TimeUnit.MINUTES.toMillis(1));
- System.setIn(in);
- if (runner.isAlive()) {
- runner.interrupt();
- fail("Runner didn't terminate properly");
+ URLConnection connection = url.openConnection();
+ connection.setReadTimeout(500);
+ connection.getInputStream();
+ fail(url.toString() + " is available");
+ } catch (Exception e) {
+ assertTrue(e.getMessage(), e instanceof IOException);
+ }
+ }
+
+ private void retry(RetryTemplate retryTemplate) throws InterruptedException {
+ Throwable error = null;
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ try {
+ retryTemplate.retry();
+ return;
+ } catch (Exception | AssertionError e) {
+ error = e;
+ Thread.sleep(RETRY_WAIT_PERIOD);
}
}
+ fail(ofNullable(error).map(Throwable::getMessage).orElse("retry failes"));
+ }
+
+ interface RetryTemplate {
+ void retry() throws Exception;
}
}
diff --git a/meecrowave-maven-plugin/src/test/java/org/app/AdditionalEndpoint.java b/meecrowave-maven-plugin/src/test/java/org/app/AdditionalEndpoint.java
new file mode 100644
index 0000000..54c524b
--- /dev/null
+++ b/meecrowave-maven-plugin/src/test/java/org/app/AdditionalEndpoint.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.app;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+@Path("additional")
+@ApplicationScoped
+public class AdditionalEndpoint {
+
+ @GET
+ @Produces(MediaType.TEXT_PLAIN)
+ public String available() {
+ return "available";
+ }
+}