/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.sa.zookeeper; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import org.apache.curator.framework.recipes.locks.InterProcessLock; /** * Singleton service that runs on only one node at a given time. * * @author jonnymiller */ public abstract class SingletonService implements Runnable { protected static final long DEFAULT_ERROR_RETRY_DELAY = 15000; protected final Logger log = Logger.getLogger(getClass()); @Autowired private CoordinatorClient coordinatorClient; private long errorRetryDelay = DEFAULT_ERROR_RETRY_DELAY; private volatile Thread thread; private InterProcessLock lock; public CoordinatorClient getCoordinatorClient() { return coordinatorClient; } public void setCoordinatorClient(CoordinatorClient coordinatorClient) { this.coordinatorClient = coordinatorClient; } public long getErrorRetryDelay() { return errorRetryDelay; } public void setErrorRetryDelay(long errorRetryDelay) { this.errorRetryDelay = errorRetryDelay; } @PostConstruct public final void start() { log.debug("Starting " + getClass().getSimpleName()); thread = new Thread(this); thread.start(); } @PreDestroy public final void stop() { log.debug("Stopping " + getClass().getSimpleName()); Thread runningThread = thread; thread = null; if (runningThread != null) { stopService(); runningThread.interrupt(); try { runningThread.join(); } catch (InterruptedException e) { log.error("Interrupted waiting for successful termination", e); } } } @Override public void run() { String serviceName = getClass().getSimpleName(); boolean error = false; try { Thread runningThread = Thread.currentThread(); thread = runningThread; while ((thread == runningThread) && !runningThread.isInterrupted()) { if (error) { log.info(String.format("Pausing %s for %dms due to error", serviceName, errorRetryDelay)); Thread.sleep(errorRetryDelay); error = false; } if (acquireLock()) { try { log.info("Acquired singleton service " + serviceName); runService(); } catch (RuntimeException e) { log.error("Singleton service " + serviceName + " failed", e); error = true; } finally { releaseLock(); log.info("Released singleton service " + serviceName); } } } } catch (InterruptedException e) { log.warn("Singleton service " + serviceName + " interrupted", e); } } private boolean acquireLock() { log.debug("Acquiring lock"); lock = coordinatorClient.getLock(getClass().getName()); return acquireLock(lock); } private void releaseLock() { releaseLock(lock); lock = null; } protected boolean acquireLock(InterProcessLock lock) { try { lock.acquire(); log.debug("Acquired lock"); return true; } catch (Exception e) { log.error("Error acquiring lock", e); return false; } } protected void releaseLock(InterProcessLock lock) { try { if (lock != null) { lock.release(); log.debug("Released lock"); } } catch (Exception e) { log.error("Error releasing lock", e); } } /** * Runs the singleton service after the lock is acquired. */ protected abstract void runService(); /** * Stops a running singleton service. */ protected abstract void stopService(); }