package co.codewizards.cloudstore.core.repo.local; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ServiceLoader; import co.codewizards.cloudstore.core.util.AssertUtil; public class LocalRepoTransactionListenerRegistry { private final LocalRepoTransaction transaction; private final List<LocalRepoTransactionListener> listeners; private static List<Class<? extends LocalRepoTransactionListener>> listenerClasses; public LocalRepoTransactionListenerRegistry(final LocalRepoTransaction transaction) { this.transaction = AssertUtil.assertNotNull(transaction, "transaction"); this.listeners = createListeners(); for (final LocalRepoTransactionListener listener : listeners) transaction.setContextObject(listener); } public LocalRepoTransaction getTransaction() { return transaction; } /** * Notifies this instance about the {@linkplain #getTransaction() transaction} being begun. * @see #onCommit() * @see #onRollback() */ public void onBegin() { for (final LocalRepoTransactionListener listener : listeners) listener.onBegin(); } /** * Notifies this instance about the {@linkplain #getTransaction() transaction} being committed. * @see #onBegin() * @see #onRollback() */ public void onCommit() { // We flush *before* triggering each listener! It's likely that flushing causes a few JDO-lifecycle-listeners to be // triggered and thus the LocalRepoTransactionListeners might work on an incomplete state, if we flushed later. // Additionally, every listener might change some data and we thus need to flush again between the listeners. transaction.flush(); for (final LocalRepoTransactionListener listener : listeners) { listener.onCommit(); transaction.flush(); } } /** * Notifies this instance about the {@linkplain #getTransaction() transaction} being rolled back. * @see #onBegin() * @see #onCommit() */ public void onRollback() { for (final LocalRepoTransactionListener listener : listeners) listener.onRollback(); } private List<LocalRepoTransactionListener> createListeners() { if (listenerClasses == null) { final List<LocalRepoTransactionListener> listeners = new LinkedList<>(); final Iterator<LocalRepoTransactionListener> iterator = ServiceLoader.load(LocalRepoTransactionListener.class).iterator(); while (iterator.hasNext()) { final LocalRepoTransactionListener listener = iterator.next(); listener.setTransaction(transaction); listeners.add(listener); } sortListeners(listeners); final List<Class<? extends LocalRepoTransactionListener>> lcl = new ArrayList<>(listeners.size()); for (final LocalRepoTransactionListener listener : listeners) lcl.add(listener.getClass()); listenerClasses = lcl; return listeners; } else { final List<LocalRepoTransactionListener> listeners = new ArrayList<>(listenerClasses.size()); for (final Class<? extends LocalRepoTransactionListener> lc : listenerClasses) { final LocalRepoTransactionListener listener = createInstance(lc); listener.setTransaction(transaction); listeners.add(listener); } return listeners; } } private void sortListeners(final List<LocalRepoTransactionListener> listeners) { Collections.sort(listeners, new Comparator<LocalRepoTransactionListener>() { @Override public int compare(final LocalRepoTransactionListener o1, final LocalRepoTransactionListener o2) { final int result = -1 * Integer.compare(o1.getPriority(), o2.getPriority()); if (result != 0) return result; return o1.getClass().getName().compareTo(o2.getClass().getName()); } }); } private static <T> T createInstance(final Class<T> clazz) { try { return clazz.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } }