package com.atlassian.labs.speakeasy.util.exec; import com.google.common.base.Function; import com.google.common.collect.MapMaker; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import static com.atlassian.labs.speakeasy.util.ExtensionValidate.isValidExtensionKey; /** * */ public abstract class KeyedSyncExecutor<T, D> { private final Map<String,ReadWriteLock> locks = new MapMaker().makeComputingMap(new Function<String,ReadWriteLock>() { public ReadWriteLock apply(String from) { return new ReentrantReadWriteLock(); } }); public final <R> R forKey(String id, D targetContext, Operation<T, R> op) { if (id == null) { try { R result = op.operateOn(null); afterSuccessfulOperation(null, result); return result; } catch (Exception e) { handleException(null, e); } } // only sync with git repo if a valid plugin key if (allowKey(id)) { ReadWriteLock readWriteLock = locks.get(id); Lock lock = op instanceof ReadOnlyOperation ? readWriteLock.readLock() : readWriteLock.writeLock(); lock.lock(); try { T target = getTarget(id, targetContext); R result = op.operateOn(target); afterSuccessfulOperation(target, result); return result; } catch (Exception e) { handleException(id, e); } finally { lock.unlock(); } } return null; } protected void afterSuccessfulOperation(T target, Object result) {} protected boolean allowKey(String id) { return true; } protected void handleException(String id, Exception ex) { if (ex instanceof RuntimeException) { throw (RuntimeException)ex; } else { throw new RuntimeException(ex); } } protected abstract T getTarget(String id, D targetContext) throws Exception; }