All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.summerb.utils.threads.RecurringBackgroundTask Maven / Gradle / Ivy

package org.summerb.utils.threads;

import java.lang.management.ManagementFactory;

import javax.management.MBeanServer;
import javax.management.ObjectName;

import org.apache.log4j.Logger;
import org.summerb.utils.exceptions.ExceptionUtils;

public class RecurringBackgroundTask implements RecurringBackgroundTaskMXBean {
	private static Logger log = Logger.getLogger(RecurringBackgroundTask.class);

	private Runnable runnable;
	private long delayMs;
	private Thread thread;
	private boolean tearDownRequested;
	private long statsIterations;
	private long statsErrors;
	private long statsInterruptions;
	private String statsLastExceptionMessage;

	public RecurringBackgroundTask(Runnable runnable, long delayMs) {
		this.runnable = runnable;
		this.delayMs = delayMs;

		String threadName = chooseThreadName(runnable);
		thread = new Thread(envelop, threadName);
		thread.setDaemon(true);
		registerMxBean(threadName);
		thread.start();
	}

	private void registerMxBean(String threadName) {
		MBeanServer server = ManagementFactory.getPlatformMBeanServer();
		try {
			String name = String.format("%s:type=BackgroundWorker,name=%s", this.getClass().getPackage().getName(),
					threadName);
			ObjectName mxBeanName = new ObjectName(name);
			if (!server.isRegistered(mxBeanName)) {
				server.registerMBean(this, new ObjectName(name));
			}
		} catch (Throwable t) {
			throw new RuntimeException("Failed to register MX bean", t);
		}
	}

	private String chooseThreadName(Runnable runnable) {
		String threadName = runnable.toString();
		threadName = threadName.substring(threadName.lastIndexOf(".") + 1);
		return threadName;
	}

	@SuppressWarnings("deprecation")
	public void tearDown(long joinTimeout) {
		tearDownRequested = true;
		thread.interrupt();
		try {
			thread.join(joinTimeout);
		} catch (InterruptedException e) {
			log.warn("Failed to gracefully tear down thread", e);
			thread.stop();
		}
	}

	private Runnable envelop = new Runnable() {
		@Override
		public void run() {
			try {
				while (!Thread.interrupted() || !tearDownRequested) {
					statsIterations++;
					try {
						// do job itself
						runnable.run();
					} catch (Throwable t) {
						statsErrors++;
						logSafe(t);
					}

					// delay
					try {
						Thread.sleep(delayMs);
					} catch (InterruptedException ie) {
						statsInterruptions++;
						if (tearDownRequested) {
							return;
						} else {
							continue;
						}
					}
				}
			} finally {
				log.info("Envelope finished for " + runnable);
			}
		}

		private void logSafe(Throwable t) {
			try {
				statsLastExceptionMessage = ExceptionUtils.getAllMessagesRaw(t);
				log.error("Iteration failed for " + runnable, t);
			} catch (Throwable exc) {
				// do nothing
			}
		}
	};

	@Override
	public boolean isTearDownRequested() {
		return tearDownRequested;
	}

	@Override
	public long getIterations() {
		return statsIterations;
	}

	@Override
	public long getErrors() {
		return statsErrors;
	}

	@Override
	public long getInterruptions() {
		return statsInterruptions;
	}

	@Override
	public String getLastExceptionMessage() {
		return statsLastExceptionMessage;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy