001package co.codewizards.cloudstore.core.repo.local; 002 003import static java.util.Objects.*; 004 005import java.util.ArrayList; 006import java.util.Collections; 007import java.util.Comparator; 008import java.util.Iterator; 009import java.util.LinkedList; 010import java.util.List; 011import java.util.ServiceLoader; 012 013public class LocalRepoTransactionListenerRegistry { 014 015 private final LocalRepoTransaction transaction; 016 017 private final List<LocalRepoTransactionListener> listeners; 018 private static List<Class<? extends LocalRepoTransactionListener>> listenerClasses; 019 020 public LocalRepoTransactionListenerRegistry(final LocalRepoTransaction transaction) { 021 this.transaction = requireNonNull(transaction, "transaction"); 022 this.listeners = createListeners(); 023 024 for (final LocalRepoTransactionListener listener : listeners) 025 transaction.setContextObject(listener); 026 } 027 028 public LocalRepoTransaction getTransaction() { 029 return transaction; 030 } 031 032 /** 033 * Notifies this instance about the {@linkplain #getTransaction() transaction} being begun. 034 * @see #onCommit() 035 * @see #onRollback() 036 */ 037 public void onBegin() { 038 for (final LocalRepoTransactionListener listener : listeners) 039 listener.onBegin(); 040 } 041 042 /** 043 * Notifies this instance about the {@linkplain #getTransaction() transaction} being committed. 044 * @see #onBegin() 045 * @see #onRollback() 046 */ 047 public void onCommit() { 048 // We flush *before* triggering each listener! It's likely that flushing causes a few JDO-lifecycle-listeners to be 049 // triggered and thus the LocalRepoTransactionListeners might work on an incomplete state, if we flushed later. 050 // Additionally, every listener might change some data and we thus need to flush again between the listeners. 051 transaction.flush(); 052 for (final LocalRepoTransactionListener listener : listeners) { 053 listener.onCommit(); 054 transaction.flush(); 055 } 056 } 057 058 /** 059 * Notifies this instance about the {@linkplain #getTransaction() transaction} being rolled back. 060 * @see #onBegin() 061 * @see #onCommit() 062 */ 063 public void onRollback() { 064 for (final LocalRepoTransactionListener listener : listeners) 065 listener.onRollback(); 066 } 067 068 private List<LocalRepoTransactionListener> createListeners() { 069 if (listenerClasses == null) { 070 final List<LocalRepoTransactionListener> listeners = new LinkedList<>(); 071 final Iterator<LocalRepoTransactionListener> iterator = ServiceLoader.load(LocalRepoTransactionListener.class).iterator(); 072 while (iterator.hasNext()) { 073 final LocalRepoTransactionListener listener = iterator.next(); 074 listener.setTransaction(transaction); 075 listeners.add(listener); 076 } 077 078 sortListeners(listeners); 079 080 final List<Class<? extends LocalRepoTransactionListener>> lcl = new ArrayList<>(listeners.size()); 081 for (final LocalRepoTransactionListener listener : listeners) 082 lcl.add(listener.getClass()); 083 084 listenerClasses = lcl; 085 return listeners; 086 } 087 else { 088 final List<LocalRepoTransactionListener> listeners = new ArrayList<>(listenerClasses.size()); 089 for (final Class<? extends LocalRepoTransactionListener> lc : listenerClasses) { 090 final LocalRepoTransactionListener listener = createInstance(lc); 091 listener.setTransaction(transaction); 092 listeners.add(listener); 093 } 094 095 return listeners; 096 } 097 } 098 099 private void sortListeners(final List<LocalRepoTransactionListener> listeners) { 100 Collections.sort(listeners, new Comparator<LocalRepoTransactionListener>() { 101 @Override 102 public int compare(final LocalRepoTransactionListener o1, final LocalRepoTransactionListener o2) { 103 final int result = -1 * Integer.compare(o1.getPriority(), o2.getPriority()); 104 if (result != 0) 105 return result; 106 107 return o1.getClass().getName().compareTo(o2.getClass().getName()); 108 } 109 }); 110 } 111 112 private static <T> T createInstance(final Class<T> clazz) { 113 try { 114 return clazz.newInstance(); 115 } catch (InstantiationException | IllegalAccessException e) { 116 throw new RuntimeException(e); 117 } 118 } 119}