package de.codecentric.batch.scheduling.concurrent;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* A SLF4J MDC-compatible {@link ThreadPoolExecutor}.
* <p>
* In general, MDC is used to store diagnostic information (e.g. logfile name) in per-thread variables, to facilitate logging. However, although MDC
* data is passed to thread children, this doesn't work when threads are reused in a thread pool. This is a drop-in replacement for
* {@link ThreadPoolTaskExecutor} sets MDC data before each task appropriately.
* </p>
*
* @author Dennis Schulte
*/
public class MdcThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private static final long serialVersionUID = 1L;
private boolean useFixedContext = false;
private Map<String, String> fixedContext;
public MdcThreadPoolTaskExecutor() {
super();
}
public MdcThreadPoolTaskExecutor(Map<String, String> fixedContext) {
super();
this.fixedContext = fixedContext;
useFixedContext = (fixedContext != null);
}
private Map<String, String> getContextForTask() {
return useFixedContext ? fixedContext : MDC.getCopyOfContextMap();
}
/**
* All executions will have MDC injected. {@code ThreadPoolExecutor}'s submission methods ({@code submit()} etc.)
* all delegate to this.
*/
@Override
public void execute(Runnable command) {
super.execute(wrap(command, getContextForTask()));
}
public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
return new Runnable() {
@Override
public void run() {
Map<String, String> previous = MDC.getCopyOfContextMap();
if (context == null) {
MDC.clear();
} else {
MDC.setContextMap(context);
}
try {
runnable.run();
} finally {
if (previous == null) {
MDC.clear();
} else {
MDC.setContextMap(previous);
}
}
}
};
}
}