Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to set timeout on selfdiagnose #20

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
### 2.8.8
- Added timeout option to selfdiagnose call
### 2.8.7
- changed msg of environment property checker
### 2.8.6
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>com.philemonworks</groupId>
<artifactId>selfdiagnose</artifactId>
<packaging>jar</packaging>
<version>2.8.7</version>
<version>2.8.8</version>
<name>SelfDiagnose</name>
<url>http://github.com/emicklei/selfdiagnose</url>
<description>SelfDiagnose - library of tasks to verify an execution environment at runtime</description>
Expand Down
105 changes: 88 additions & 17 deletions src/main/java/com/philemonworks/selfdiagnose/SelfDiagnose.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.philemonworks.selfdiagnose;

import com.philemonworks.selfdiagnose.check.ReportStaticMessageTask;
import com.philemonworks.selfdiagnose.output.DiagnoseRun;
import com.philemonworks.selfdiagnose.output.DiagnoseRunReporter;
import com.philemonworks.selfdiagnose.output.XMLReporter;
Expand All @@ -30,6 +31,7 @@
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.*;

/**
* SelfDiagnose is the component that keeps a registration of DiagnosticTasks
Expand All @@ -51,14 +53,24 @@ public abstract class SelfDiagnose {
/**
* The name of the resource that holds the specification of tasks.
*/
public final static String VERSION = "2.8.7";
public final static String VERSION = "2.8.8";
public final static String COPYRIGHT = "(c) ernestmicklei.com";
public final static String CONFIG = "selfdiagnose.xml";
private static URL CONFIG_URL = null; // will be initialized by configure(...)
private final static Logger LOG = Logger.getLogger(SelfDiagnose.class);

private static List<DiagnosticTask> tasks = Collections.synchronizedList(new ArrayList<DiagnosticTask>());

// threads are removed from the pool if no longer used.
static final ExecutorService SharedPool = Executors.newCachedThreadPool(new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread runner = new Thread(r);
runner.setDaemon(true);
runner.setName("SelfDiagnose.SelfDiagnoseTimoutRunner");
return runner;
}
});

static {
SelfDiagnose.configure(CONFIG);
}
Expand Down Expand Up @@ -181,39 +193,68 @@ public static DiagnoseRun runTasks() {
* Basic method to run all registered tasks.
*/
public static DiagnoseRun runTasks(DiagnoseRunReporter reporter) {
return SelfDiagnose.runTasks(tasks, reporter, new ExecutionContext());
return SelfDiagnose.runTasks(tasks, reporter, new ExecutionContext(), null);
}

/**
* Basic method to run all registered tasks.
*/
public static DiagnoseRun runTasks(DiagnoseRunReporter reporter, Integer timeout) {
return SelfDiagnose.runTasks(tasks, reporter, new ExecutionContext(), timeout);
}

/**
* Basic method to run all registered tasks.
*/
public static DiagnoseRun runTasks(DiagnoseRunReporter reporter, ExecutionContext ctx) {
return SelfDiagnose.runTasks(tasks, reporter, ctx);
return SelfDiagnose.runTasks(tasks, reporter, ctx, null);
}

/**
* Basic method to the tasks provided
*/
public static DiagnoseRun runTasks(List<DiagnosticTask> taskList, DiagnoseRunReporter reporter, ExecutionContext ctx) {
DiagnoseRun run = new DiagnoseRun();
List<DiagnosticTaskResult> results = new ArrayList<DiagnosticTaskResult>(taskList.size());
for (int i = 0; i < taskList.size(); i++) {
DiagnosticTask each = (DiagnosticTask) taskList.get(i);
DiagnosticTaskResult result = null;
// see if task wants to run with a timeout
if (each.needsLimitedRuntime()) {
result = new TaskBackgroundRunner().runWithin(each, ctx, each.getTimeoutInMilliSeconds());
} else {
result = each.run(ctx);
return runTasks(taskList, reporter, ctx, null);
}

public static DiagnoseRun runTasks(final DiagnoseRunReporter reporter, final ExecutionContext ctx, final Integer timeout) {
return runTasks(tasks, reporter, ctx, timeout);
}

public static DiagnoseRun runTasks(final List<DiagnosticTask> taskList, final DiagnoseRunReporter reporter, final ExecutionContext ctx, final Integer timeout) {
TaskRunner resultTask = new TaskRunner(taskList, ctx);
List<DiagnosticTaskResult> results = null;
if (timeout == null) {
results = resultTask.call();
} else {
final Future<List<DiagnosticTaskResult>> future = SharedPool.submit(resultTask);
try {
results = future.get(timeout, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
results = createErrorResult("Interrupted while checking");
} catch (ExecutionException e) {
throw (RuntimeException) e.getCause(); // because task itself doesn't throw CheckedExceptions
} catch (TimeoutException e) {
future.cancel(true);
results = createErrorResult("Could not execute all SelfDiagnose tasks within specified limit of " + timeout + " ms.");
}
result.addToResults(results);
}
run.finished();

DiagnoseRun run = new DiagnoseRun();
run.results = results;
run.finished();
reporter.report(run);
return run;
}

private static List<DiagnosticTaskResult> createErrorResult(String message) {
List<DiagnosticTaskResult> result = new ArrayList<DiagnosticTaskResult>(1);
final ReportStaticMessageTask task = new ReportStaticMessageTask(false, message, "System Error Message");

result.add(task.run());
return result;
}

/**
* Run all registered DiagnosticTasks and report to the LOG. After running
* all tasks, some simple statistics are logged.
Expand All @@ -239,10 +280,40 @@ public static List<DiagnosticTask> getTasks() {

/**
* Remove the previously registered task. Ignore if was not present.
*
* @param custom
*/
public static void unregister(DiagnosticTask task) {
tasks.remove(task);
}

private static class TaskRunner implements Callable<List<DiagnosticTaskResult>> {
private final List<DiagnosticTask> taskList;
private final ExecutionContext ctx;

public TaskRunner(List<DiagnosticTask> taskList, ExecutionContext ctx) {
this.taskList = taskList;
this.ctx = ctx;
}

@Override
public List<DiagnosticTaskResult> call() {
List<DiagnosticTaskResult> results = new ArrayList<DiagnosticTaskResult>(taskList.size());
for (int i = 0; i < taskList.size(); i++) {
if (Thread.interrupted()) {
// Probably because future is cancelled. Don't continue scheduling/executing new tasks
return results;
}

DiagnosticTask each = (DiagnosticTask) taskList.get(i);
DiagnosticTaskResult result = null;
// see if task wants to run with a timeout
if (each.needsLimitedRuntime()) {
result = new TaskBackgroundRunner().runWithin(each, ctx, each.getTimeoutInMilliSeconds());
} else {
result = each.run(ctx);
}
result.addToResults(results);
}
return results;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,16 @@
*/
package com.philemonworks.selfdiagnose;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import com.philemonworks.selfdiagnose.output.*;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.philemonworks.selfdiagnose.output.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;

/**
* SelfDiagnoseServlet is a server component that can run and report on all registered diagnostic tasks.<br/>
Expand Down Expand Up @@ -115,6 +114,7 @@ public void run(HttpServletRequest req, HttpServletResponse resp) throws Servlet
if (format == null)
format = "html";
DiagnoseRunReporter reporter = getReporter(format);
Integer timeout = getTimeout(req);
String content = "?";
try {
// bring request available to thread
Expand All @@ -124,7 +124,8 @@ public void run(HttpServletRequest req, HttpServletResponse resp) throws Servlet
ctx.setValue("servletcontext", this.getServletContext());
} catch (Exception ex) {
} // bummer
DiagnoseRun run = SelfDiagnose.runTasks(reporter, ctx);
DiagnoseRun run;
run = SelfDiagnose.runTasks(reporter, ctx, timeout);
resp.setHeader("X-SelfDiagnose-OK", Boolean.toString(run.isOK()));
content = reporter.getContent();
} finally {
Expand All @@ -150,6 +151,22 @@ public DiagnoseRunReporter getReporter(String format) {
throw new IllegalArgumentException("Unkown format for reporting:" + format);
}

/**
* Read the timeout in ms from the request, if any.
*
* @return the timeout for the selfdiagnose task in milliseconds,
* or null if no timeout is applicable, or the timeout < 1 ms.
* @throws NumberFormatException if the timeout value is not a number.
*/
protected Integer getTimeout(HttpServletRequest req) {
String timeout = req.getParameter("timeoutMS");
if (timeout != null) {
int timeoutMS = Integer.valueOf(timeout);
return timeoutMS > 0 ? timeoutMS : null;
}
return null;
}

/**
* Accept the configuration that is POSTed by the request.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.philemonworks.selfdiagnose.check;

import com.philemonworks.selfdiagnose.DiagnoseException;
import com.philemonworks.selfdiagnose.DiagnosticTask;
import com.philemonworks.selfdiagnose.DiagnosticTaskResult;
import com.philemonworks.selfdiagnose.ExecutionContext;

/**
* Class to report a message with a given status.
* <p>
* This task doesn't actually check anything, it just adds a line to the report.
*/
public class ReportStaticMessageTask extends DiagnosticTask {

private final boolean passed;
private final String message;
private final String description;

/**
* @param passed true and this check is considered to be passed, otherwise failed.
* @param message The message to display
*/
public ReportStaticMessageTask(boolean passed, String message, String description) {
super();

this.passed = passed;
this.message = message;
this.description = description;
}

@Override
public String getDescription() {
return description;
}

@Override
public void run(ExecutionContext ctx, DiagnosticTaskResult result) throws DiagnoseException {
if (passed) {
result.setPassedMessage(message);
} else {
result.setFailedMessage(message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class SelfDiagnoseResource implements ApplicationContextAware {
@GET
@Produces("text/html,application/xml,application/json")
@Consumes("text/html,application/xml")
public Response runAndReportResults(@PathParam("extension") String format, @QueryParam("format") String formatOverride) {
public Response runAndReportResults(@PathParam("extension") String format, @QueryParam("format") String formatOverride, @QueryParam("timeoutMS") Integer timeout) {
DiagnoseRunReporter reporter;
ResponseBuilder builder = Response.ok();
if (".xml".equals(format) || "xml".equals(formatOverride)) {
Expand All @@ -49,7 +49,7 @@ public Response runAndReportResults(@PathParam("extension") String format, @Quer
reporter = new HTMLReporter();
builder.header("Content-Type", "text/html");
}
DiagnoseRun run = SelfDiagnose.runTasks(reporter);
DiagnoseRun run = SelfDiagnose.runTasks(reporter, timeout);
builder.header("X-SelfDiagnose-OK", run.isOK());
return builder.entity(reporter.getContent()).build();
}
Expand All @@ -67,7 +67,7 @@ public Response runSubmittedTasks(@PathParam("extension") String format, InputSt
} catch (Exception e) {
return Response.serverError().entity(e.getCause().getMessage()).build();
}
return this.runAndReportResults(format, null);
return this.runAndReportResults(format, null, null);
}

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,5 @@ public void testReload() {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Loading