Skip to content

Commit

Permalink
#782: Polished
Browse files Browse the repository at this point in the history
  • Loading branch information
davsclaus committed Dec 5, 2013
1 parent b8d8e0e commit a655d36
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 126 deletions.
102 changes: 5 additions & 97 deletions hawtio-maven-plugin/src/main/java/io/hawt/maven/BaseMojo.java
Expand Up @@ -34,7 +34,7 @@

public abstract class BaseMojo extends AbstractMojo {

private long daemonThreadJoinTimeout = 15000L;
MojoLifecycle mojoLifecycle;

@Component
MavenProject project;
Expand Down Expand Up @@ -76,6 +76,8 @@ public abstract class BaseMojo extends AbstractMojo {
String extendedPluginDependencyArtifactId;

protected void doBeforeExecute() {
mojoLifecycle = createMojoLifecycle();

if (offline) {
getLog().info("hawtio is running in offline mode");
System.setProperty("hawtio.offline", "true");
Expand All @@ -86,6 +88,8 @@ protected void doAfterExecute() {
System.clearProperty("hawtio.offline");
}

protected abstract MojoLifecycle createMojoLifecycle();

/**
* Set up a classloader for the execution of the main class.
*
Expand Down Expand Up @@ -360,100 +364,4 @@ protected Set<Artifact> resolveExecutableDependencies(Artifact executablePomArti
return executableDependencies;
}

protected void joinNonDaemonThreads(ThreadGroup threadGroup) {
boolean foundNonDaemon;
do {
foundNonDaemon = false;
Collection<Thread> threads = getActiveThreads(threadGroup);
for (Thread thread : threads) {
if (thread.isDaemon()) {
continue;
}
foundNonDaemon = true; // try again; maybe more threads were
// created while we were busy
joinThread(thread, 0);
}
} while (foundNonDaemon);
}

protected void joinThread(Thread thread, long timeoutMsecs) {
try {
getLog().info("Joining on thread " + thread);
thread.join(timeoutMsecs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // good practice if don't throw
getLog().warn("interrupted while joining against thread " + thread, e); // not
// expected!
}
// generally abnormal
if (thread.isAlive()) {
getLog().warn("thread " + thread + " was interrupted but is still alive after waiting at least "
+ timeoutMsecs + "msecs");
}
}

protected Collection<Thread> getActiveThreads(ThreadGroup threadGroup) {
Thread[] threads = new Thread[threadGroup.activeCount()];
int numThreads = threadGroup.enumerate(threads);
Collection<Thread> result = new ArrayList<Thread>(numThreads);
for (int i = 0; i < threads.length && threads[i] != null; i++) {
result.add(threads[i]);
}
// note: result should be modifiable
return result;
}

@SuppressWarnings("deprecation")
protected void terminateThreads(ThreadGroup threadGroup) {
long startTime = System.currentTimeMillis();
Set<Thread> uncooperativeThreads = new HashSet<Thread>(); // these were not responsive
// to interruption
for (Collection<Thread> threads = getActiveThreads(threadGroup); !threads.isEmpty(); threads = getActiveThreads(threadGroup), threads
.removeAll(uncooperativeThreads)) {
// Interrupt all threads we know about as of this instant (harmless
// if spuriously went dead (! isAlive())
// or if something else interrupted it ( isInterrupted() ).
for (Thread thread : threads) {
getLog().debug("interrupting thread " + thread);
thread.interrupt();
}
// Now join with a timeout and call stop() (assuming flags are set
// right)
for (Thread thread : threads) {
if (!thread.isAlive()) {
continue; // and, presumably it won't show up in
// getActiveThreads() next iteration
}
if (daemonThreadJoinTimeout <= 0) {
joinThread(thread, 0); // waits until not alive; no timeout
continue;
}
long timeout = daemonThreadJoinTimeout - (System.currentTimeMillis() - startTime);
if (timeout > 0) {
joinThread(thread, timeout);
}
if (!thread.isAlive()) {
continue;
}
uncooperativeThreads.add(thread); // ensure we don't process
getLog().warn("thread " + thread + " will linger despite being asked to die via interruption");
}
}
if (!uncooperativeThreads.isEmpty()) {
getLog().warn("NOTE: "
+ uncooperativeThreads.size()
+ " thread(s) did not finish despite being asked to "
+ " via interruption. This is not a problem with exec:java, it is a problem with the running code."
+ " Although not serious, it should be remedied.");
} else {
int activeCount = threadGroup.activeCount();
if (activeCount != 0) {
Thread[] threadsArray = new Thread[1];
threadGroup.enumerate(threadsArray);
getLog().debug("strange; " + activeCount + " thread(s) still active in the group "
+ threadGroup + " such as " + threadsArray[0]);
}
}
}

}
@@ -0,0 +1,41 @@
/**
* 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 io.hawt.maven;

import java.util.concurrent.CountDownLatch;

import org.apache.maven.plugin.logging.Log;

public class CountDownLatchMojoLifecycle extends DefaultMojoLifecycle {

private final CountDownLatch latch;

public CountDownLatchMojoLifecycle(Log log, CountDownLatch latch) {
super(log);
this.latch = latch;
}

@Override
public void join(ThreadGroup threadGroup) {
try {
latch.await();
} catch (InterruptedException e) {
// ignore
}
}

}
@@ -0,0 +1,132 @@
/**
* 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 io.hawt.maven;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.apache.maven.plugin.logging.Log;

public class DefaultMojoLifecycle implements MojoLifecycle {

private long daemonThreadJoinTimeout = 15000L;
private final Log log;

public DefaultMojoLifecycle(Log log) {
this.log = log;
}

@Override
public void join(ThreadGroup threadGroup) {
boolean foundNonDaemon;
do {
foundNonDaemon = false;
Collection<Thread> threads = getActiveThreads(threadGroup);
for (Thread thread : threads) {
if (thread.isDaemon()) {
continue;
}
foundNonDaemon = true; // try again; maybe more threads were
// created while we were busy
joinThread(thread, 0);
}
} while (foundNonDaemon);
}

@Override
public void terminateThreads(ThreadGroup threadGroup) {
long startTime = System.currentTimeMillis();
Set<Thread> uncooperativeThreads = new HashSet<Thread>(); // these were not responsive
// to interruption
for (Collection<Thread> threads = getActiveThreads(threadGroup); !threads.isEmpty(); threads = getActiveThreads(threadGroup), threads
.removeAll(uncooperativeThreads)) {
// Interrupt all threads we know about as of this instant (harmless
// if spuriously went dead (! isAlive())
// or if something else interrupted it ( isInterrupted() ).
for (Thread thread : threads) {
log.debug("interrupting thread " + thread);
thread.interrupt();
}
// Now join with a timeout and call stop() (assuming flags are set
// right)
for (Thread thread : threads) {
if (!thread.isAlive()) {
continue; // and, presumably it won't show up in
// getActiveThreads() next iteration
}
if (daemonThreadJoinTimeout <= 0) {
joinThread(thread, 0); // waits until not alive; no timeout
continue;
}
long timeout = daemonThreadJoinTimeout - (System.currentTimeMillis() - startTime);
if (timeout > 0) {
joinThread(thread, timeout);
}
if (!thread.isAlive()) {
continue;
}
uncooperativeThreads.add(thread); // ensure we don't process
log.warn("thread " + thread + " will linger despite being asked to die via interruption");
}
}
if (!uncooperativeThreads.isEmpty()) {
log.warn("NOTE: "
+ uncooperativeThreads.size()
+ " thread(s) did not finish despite being asked to "
+ " via interruption. This is not a problem with exec:java, it is a problem with the running code."
+ " Although not serious, it should be remedied.");
} else {
int activeCount = threadGroup.activeCount();
if (activeCount != 0) {
Thread[] threadsArray = new Thread[1];
threadGroup.enumerate(threadsArray);
log.debug("strange; " + activeCount + " thread(s) still active in the group "
+ threadGroup + " such as " + threadsArray[0]);
}
}
}

private void joinThread(Thread thread, long timeoutMsecs) {
try {
log.info("Joining on thread " + thread);
thread.join(timeoutMsecs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // good practice if don't throw
log.warn("interrupted while joining against thread " + thread, e); // not
// expected!
}
// generally abnormal
if (thread.isAlive()) {
log.warn("thread " + thread + " was interrupted but is still alive after waiting at least "
+ timeoutMsecs + "msecs");
}
}

private Collection<Thread> getActiveThreads(ThreadGroup threadGroup) {
Thread[] threads = new Thread[threadGroup.activeCount()];
int numThreads = threadGroup.enumerate(threads);
Collection<Thread> result = new ArrayList<Thread>(numThreads);
for (int i = 0; i < threads.length && threads[i] != null; i++) {
result.add(threads[i]);
}
// note: result should be modifiable
return result;
}

}
24 changes: 24 additions & 0 deletions hawtio-maven-plugin/src/main/java/io/hawt/maven/MojoLifecycle.java
@@ -0,0 +1,24 @@
/**
* 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 io.hawt.maven;

public interface MojoLifecycle {

void join(ThreadGroup threadGroup);

void terminateThreads(ThreadGroup threadGroup);
}
9 changes: 7 additions & 2 deletions hawtio-maven-plugin/src/main/java/io/hawt/maven/RunMojo.java
Expand Up @@ -38,6 +38,11 @@ public class RunMojo extends BaseMojo {

ClassLoader classLoader;

@Override
protected MojoLifecycle createMojoLifecycle() {
return new DefaultMojoLifecycle(getLog());
}

@Override
public void execute() throws MojoExecutionException, MojoFailureException {
// use hawtio-app
Expand Down Expand Up @@ -151,10 +156,10 @@ public void run() {
bootstrapThread.setContextClassLoader(classLoader);

bootstrapThread.start();
joinNonDaemonThreads(threadGroup);
mojoLifecycle.join(threadGroup);

try {
terminateThreads(threadGroup);
mojoLifecycle.terminateThreads(threadGroup);
threadGroup.destroy();
} catch (IllegalThreadStateException e) {
getLog().warn("Cannot destroy thread group " + threadGroup, e);
Expand Down

0 comments on commit a655d36

Please sign in to comment.