package br.ufrgs.inf.prosoft.tigris.monitoring.util.threads;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
/**
* The type Verbose runnable.
*/
@SuppressWarnings("PMD.DoNotUseThreads")
public final class VerboseRunnable implements Runnable {
/**
* Original runnable.
*/
private final transient Runnable origin;
/**
* Rethrow exceptions (TRUE) or swallow them?
*/
private final transient boolean rethrow;
/**
* Shall we report a full stacktrace?
*/
private final transient boolean verbose;
/**
* The Logger.
*/
Logger logger = LoggerFactory.getLogger(NamedThreads.class);
/**
* Default constructor, doesn't swallow exceptions.
*
* @param runnable Runnable to wrap
*/
public VerboseRunnable(final Runnable runnable) {
this(runnable, false);
}
/**
* Default constructor, doesn't swallow exceptions.
*
* @param callable Callable to wrap
* @since 0.7.17
*/
public VerboseRunnable(final Callable<?> callable) {
this(callable, false);
}
/**
* Default constructor, doesn't swallow exceptions.
*
* @param callable Callable to wrap
* @param swallow Shall we swallow exceptions ({@code TRUE}) or re-throw ({@code FALSE})? Exception swallowing means that {@link #run()} will never throw any exceptions (in any case all exceptions are logged using {@link Logger}.
* @since 0.1.10
*/
public VerboseRunnable(final Callable<?> callable, final boolean swallow) {
this(callable, swallow, true);
}
/**
* Default constructor.
*
* @param callable Callable to wrap
* @param swallow Shall we swallow exceptions ({@code TRUE}) or re-throw ({@code FALSE})? Exception swallowing means that {@link #run()} will never throw any exceptions (in any case all exceptions are logged using {@link Logger}.
* @param vrbs Shall we report the entire stacktrace of the exception ({@code TRUE}) or just its message in one line ({@code FALSE})
* @since 0.7.17
*/
@SuppressWarnings("PMD.AvoidCatchingGenericException")
public VerboseRunnable(final Callable<?> callable,
final boolean swallow, final boolean vrbs) {
this(
new Runnable() {
@Override
public void run() {
try {
callable.call();
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(ex);
// @checkstyle IllegalCatch (1 line)
} catch (final Exception ex) {
throw new IllegalStateException(ex);
}
}
@Override
public String toString() {
return callable.toString();
}
},
swallow,
vrbs
);
}
/**
* Default constructor, with configurable behavior for exceptions.
*
* @param runnable Runnable to wrap
* @param swallow Shall we swallow exceptions ({@code TRUE}) or re-throw ({@code FALSE})? Exception swallowing means that {@link #run()} will never throw any exceptions (in any case all exceptions are logged using {@link Logger}.
* @since 0.1.4
*/
public VerboseRunnable(final Runnable runnable, final boolean swallow) {
this(runnable, swallow, true);
}
/**
* Default constructor, with fully configurable behavior.
*
* @param runnable Runnable to wrap
* @param swallow Shall we swallow exceptions ({@code TRUE}) or re-throw ({@code FALSE})? Exception swallowing means that {@link #run()} will never throw any exceptions (in any case all exceptions are logged using {@link Logger}.
* @param vrbs Shall we report the entire stacktrace of the exception ({@code TRUE}) or just its message in one line ({@code FALSE})
* @since 0.7.17
*/
@SuppressWarnings("PMD.BooleanInversion")
public VerboseRunnable(final Runnable runnable,
final boolean swallow, final boolean vrbs) {
this.origin = runnable;
this.rethrow = !swallow;
this.verbose = vrbs;
}
/**
* {@inheritDoc}
* <p>
* <p>We catch {@link RuntimeException} and {@link Error} here. All other
* types of exceptions are "checked exceptions" and won't be thrown out
* of {@code Runnable#run()} method.
*/
@Override
@SuppressWarnings("PMD.AvoidCatchingGenericException")
public void run() {
try {
this.origin.run();
// @checkstyle IllegalCatch (1 line)
} catch (final RuntimeException ex) {
if (this.rethrow) {
logger.warn("escalated exception: %s", this.tail(ex));
throw ex;
}
logger.warn("swallowed exception: %s", this.tail(ex));
// @checkstyle IllegalCatch (1 line)
} catch (final Error error) {
if (this.rethrow) {
logger.error("escalated error: %s", this.tail(error));
throw error;
}
logger.error("swallowed error: %s", this.tail(error));
}
try {
TimeUnit.MICROSECONDS.sleep(1L);
} catch (final InterruptedException ex) {
Thread.currentThread().interrupt();
throw new IllegalStateException(ex);
}
}
/**
* Make a tail of the error/warning message, using the exception thrown.
*
* @param throwable The exception/error caught
* @return The message to show in logs
*/
private String tail(final Throwable throwable) {
final String tail;
if (this.verbose) {
tail = String.format("%[exception]s", throwable);
} else {
tail = String.format(
"%[type]s('%s')",
throwable,
throwable.getMessage()
);
}
return tail;
}
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof VerboseRunnable)) return false;
final VerboseRunnable other = (VerboseRunnable) o;
if (!other.canEqual((Object) this)) return false;
return true;
}
public int hashCode() {
int result = 1;
return result;
}
/**
* Can equal boolean.
*
* @param other the other
* @return the boolean
*/
protected boolean canEqual(Object other) {
return other instanceof VerboseRunnable;
}
public String toString() {
return "br.ufrgs.inf.prosoft.adaptivecaching.monitoring.util.VerboseRunnable(logger=" + this.logger + ", origin=" + this.origin + ", rethrow=" + this.rethrow + ", verbose=" + this.verbose + ")";
}
}