package org.sef4j.core.helpers.export.senders;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.sef4j.core.helpers.export.ExportFragment;
import org.sef4j.core.helpers.export.ExportFragmentList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* helper class for retryable fragments export support:
* - keep lastest value of each identifiable fragments
* - keep recent history (example: 10 last failed) for non-identifiable fragments
*
* @param <T> type of fragments to export (example: String for JSon fragments)
*/
public class RetryableFragmentsHistory<T> {
private static final Logger LOG = LoggerFactory.getLogger(RetryableFragmentsHistory.class);
public static final int DEFAULT_RETRY_HISTORY_LEN = 5;
private Map<Object,ExportFragment<T>> retryIdentifiableFragments = new HashMap<Object,ExportFragment<T>>();
private int retryHistoryLen = DEFAULT_RETRY_HISTORY_LEN;
/** optim computed field = sum_i retryNoIdFragmentsRecentHistory.get(i).size() */
private int retryNoIdFragmentsRecentHistoryLenSum = 0;
// recent history of failed fragment... each list may(should) be empty!...
// optimized storage as a cyclic array (using modulo + current modulo position)
private int retryRecentHistoryIndex;
private List<ExportFragment<T>>[] retryNoIdFragmentsRecentHistory;
// ------------------------------------------------------------------------
@SuppressWarnings("unchecked")
public RetryableFragmentsHistory(int retryHistoryLen) {
this.retryHistoryLen = retryHistoryLen;
this.retryNoIdFragmentsRecentHistory = new List[retryHistoryLen];
}
// ------------------------------------------------------------------------
public ExportFragmentList<T> shiftAndCollectRetryFragmentsToExport() {
ExportFragmentList<T> res = new ExportFragmentList<T>();
if (! retryIdentifiableFragments.isEmpty()) {
res.addAllEntries(retryIdentifiableFragments.values());
}
if (retryNoIdFragmentsRecentHistoryLenSum != 0) {
// collect all
for(int i = retryRecentHistoryIndex-1; i != retryRecentHistoryIndex;
i = modulo(i + retryHistoryLen - 1)) {
List<ExportFragment<T>> nthPastRetry = retryNoIdFragmentsRecentHistory[i];
if (nthPastRetry != null && !nthPastRetry.isEmpty()) {
res.addAllEntries(nthPastRetry);
}
}
// shift + truncate last (insert null), and update corresponding count
retryRecentHistoryIndex = modulo(retryRecentHistoryIndex + 1);
List<ExportFragment<T>> last = retryNoIdFragmentsRecentHistory[retryRecentHistoryIndex];
if (last != null) {
retryNoIdFragmentsRecentHistoryLenSum -= last.size();
}
retryNoIdFragmentsRecentHistory[retryRecentHistoryIndex] = null;
}
return res;
}
private int modulo(int i) {
assert -retryHistoryLen <= i && i <= 2*retryHistoryLen-1;
// return i % retryHistoryLen;
if (i >= retryHistoryLen) return i - retryHistoryLen;
else if (i < 0) return i + retryHistoryLen;
else return i;
}
public ExportFragmentList<T> mergeWithPrevRetryable(ExportFragmentList<T> currFragments) {
ExportFragmentList<T> res = shiftAndCollectRetryFragmentsToExport();
res.addAll(currFragments);
return res;
}
public void addShiftedFailedRetryable(ExportFragmentList<T> failedFragments) {
List<ExportFragment<T>> ls = retryNoIdFragmentsRecentHistory[retryRecentHistoryIndex];
if (ls == null) {
ls = new ArrayList<ExportFragment<T>>();
retryNoIdFragmentsRecentHistory[retryRecentHistoryIndex] = ls;
}
for(ExportFragment<T> fragment : failedFragments.toList()) {
Object id = fragment.getId();
if (id != null) {
ExportFragment<T> prev = retryIdentifiableFragments.put(id, fragment);
if (prev != null) {
LOG.debug("handle export fragment error on id '" + id + "' : override by last value to retry later");
//TODO Add error management ??!!
// try {
// fragment.getProvider().onOverrideIdentifiableFragment(fragment, prev);
// } catch(Exception ex) {
// LOG.error("Failed for failedFragment onOverrideIdentifiableFragment() ex=" + ex.getMessage() + " ... ignore, no rethrow!");
// }
}
} else {
ls.add(fragment);
}
//TODO Add error management ??!!
// try {
// fragment.getProvider().onExportFragmentFailed(fragment);
// } catch(Exception ex) {
// LOG.error("Failed onExportFragmentFailed() ex=" + ex.getMessage() + " ... ignore, no rethrow!");
// }
}
}
// ------------------------------------------------------------------------
@Override
public String toString() {
return "RetryableFragmentHistory["
+ ((! retryIdentifiableFragments.isEmpty())?
"" + retryIdentifiableFragments.size() + " elt(s)" : "")
+ ((retryNoIdFragmentsRecentHistoryLenSum != 0)?
" " + retryNoIdFragmentsRecentHistoryLenSum + " elt(s) no-id" : "")
+ "]";
}
}