package org.ei.drishti.common.audit; import org.joda.time.DateTime; import org.motechproject.util.DateUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import java.text.MessageFormat; import java.util.*; import java.util.concurrent.locks.ReentrantLock; import static java.util.Collections.binarySearch; import static org.ei.drishti.common.audit.AuditMessageType.NORMAL; @Scope("singleton") @Component public class Auditor { private List<AuditMessage> messages; private final int numberOfAuditMessagesToHoldOnTo; private static long messageIndex = DateTime.now().getMillis(); private static ReentrantLock lock = new ReentrantLock(); private static Logger logger = LoggerFactory.getLogger(Auditor.class.toString()); @Autowired public Auditor(@Value("#{drishti['number.of.audit.messages']}") int numberOfAuditMessagesToHoldOnTo) { this.messages = new ArrayList<>(); this.numberOfAuditMessagesToHoldOnTo = numberOfAuditMessagesToHoldOnTo; } public AuditMessageBuilder audit(AuditMessageType type) { return new AuditMessageBuilder(this, type); } public List<AuditMessage> messagesSince(long messageIndex) { if (messageIndex <= 0) { return messages; } int index = binarySearch(messages, new AuditMessage(DateUtil.now(), messageIndex, NORMAL, null)); int position = Math.abs(index + 1); if (position >= messages.size()) { return Collections.emptyList(); } return messages.subList(position, messages.size()); } private void prune() { if (messages.size() > numberOfAuditMessagesToHoldOnTo) { messages.remove(0); } } private void createAuditMessage(AuditMessageType messageType, Map<String, String> data) { lock.lock(); try { AuditMessage auditMessage = new AuditMessage(DateUtil.now(), messageIndex, messageType, data); messages.add(auditMessage); messageIndex++; logger.debug(MessageFormat.format("Added message: {0}", auditMessage)); prune(); } finally { lock.unlock(); } } public static class AuditMessageBuilder { private final Auditor auditor; private final AuditMessageType type; private Map<String, String> extraData; public AuditMessageBuilder(Auditor auditor, AuditMessageType type) { this.auditor = auditor; this.type = type; this.extraData = new HashMap<>(); } public AuditMessageBuilder with(String key, String value) { if (!type.supports(key)) { throw new ForbiddenFieldInAuditMessage(type, key, value); } extraData.put(key, value); return this; } public void done() { auditor.createAuditMessage(type, extraData); } } }