package org.sharegov.cirm.stats; import org.sharegov.cirm.CirmTransaction; import org.sharegov.cirm.CirmTransactionEvent; import org.sharegov.cirm.CirmTransactionListener; import org.sharegov.cirm.stats.CirmStatistics.StatsValue; /** * CirmDataProvider allows for simplified and minimum blocking data reporting to a CirmStatistics object (e.g. from Refs or MDRefs). * Calls inside CirmTransactions are automatically routed in a way, that data only is inserted into stats only on transaction success or failure, * thereby ignoring retries. * * THREAD SAFE and fast (but mostly used in one thread only. e.g worker thread). * * @author Thomas Hilpold * */ public class CirmStatsDataReporter { private final CirmStatistics stats; private final String component; public CirmStatsDataReporter(CirmStatistics stats, String component) { if (stats == null) throw new NullPointerException("stats"); if (component == null) throw new NullPointerException("component"); this.stats = stats; this.component = component; } /** * Reports success to CirmStatistics, even inside of a CirmTransaction. * In a CirmTransaction, the report will be delayed until CirmTransaction fails or succeeds. * * @param action * @param type * @param id */ public void succeeded(final String action, final String type, final String id) { final StatsValue val = stats.getEntry(component, action, type); if (CirmTransaction.isExecutingOnThisThread()) { //inside transaction, delay delivery to cirmStatistics until all potential retries are over and the //transaction either succeeded or failed. CirmTransactionListener componentDataInTransactionProvider = new CirmTransactionListener() { @Override public void transactionStateChanged(CirmTransactionEvent e) { if (e.isSucceeded()) { val.addSuccess(id); } else { val.addFailure(id, "component reported success, but transaction failed", "CirmTransaction failure"); } } }; CirmTransaction.get().addTopLevelEventListener(componentDataInTransactionProvider); } else { //no top level transaction val.addSuccess(id); } } /** * Reports a failure to CirmStatistics, even inside of a CirmTransaction. * In a CirmTransaction, the report will be delayed until CirmTransaction fails or succeeds. * * @param action * @param type * @param id * @param exception * @param failureMessage */ public void failed(final String action, final String type, final String id, final String exception, final String failureMessage) { final StatsValue val = stats.getEntry(component, action, type); if (CirmTransaction.isExecutingOnThisThread()) { //inside transaction, delay delivery to cirmStatistics until all potential retries are over and the //transaction either succeeded or failed. CirmTransactionListener componentDataInTransactionProvider = new CirmTransactionListener() { @Override public void transactionStateChanged(CirmTransactionEvent e) { if (e.isSucceeded()) { val.addFailure(id, exception, failureMessage); } else { val.addFailure(id, exception, "CirmTransaction failed and component reported failure: " + failureMessage); } } }; CirmTransaction.get().addTopLevelEventListener(componentDataInTransactionProvider); } else { //no top level transaction val.addFailure(id, exception, failureMessage); } } }