diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/InMemoryAuditEventRepository.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/InMemoryAuditEventRepository.java index a7dc37b5ab7..b17c76aa742 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/InMemoryAuditEventRepository.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/audit/InMemoryAuditEventRepository.java @@ -16,54 +16,67 @@ package org.springframework.boot.actuate.audit; -import java.util.ArrayList; -import java.util.Collections; import java.util.Date; -import java.util.HashMap; +import java.util.LinkedList; import java.util.List; -import java.util.Map; /** * In-memory {@link AuditEventRepository} implementation. * * @author Dave Syer + * @author Phillip Webb */ public class InMemoryAuditEventRepository implements AuditEventRepository { - private int capacity = 100; + private static final int DEFAULT_CAPACITY = 4000; - private final Map> events = new HashMap>(); + /** + * Circular buffer of the event with tail pointing to the last element. + */ + private AuditEvent[] events; + + private volatile int tail = -1; + + public InMemoryAuditEventRepository() { + this(DEFAULT_CAPACITY); + } + + public InMemoryAuditEventRepository(int capacity) { + this.events = new AuditEvent[capacity]; + } /** * @param capacity the capacity to set */ - public void setCapacity(int capacity) { - this.capacity = capacity; + public synchronized void setCapacity(int capacity) { + this.events = new AuditEvent[capacity]; } @Override - public List find(String principal, Date after) { - synchronized (this.events) { - return Collections.unmodifiableList(getEvents(principal)); - } - } - - private List getEvents(String principal) { - if (!this.events.containsKey(principal)) { - this.events.put(principal, new ArrayList()); - } - return this.events.get(principal); - } - - @Override - public void add(AuditEvent event) { - synchronized (this.events) { - List list = getEvents(event.getPrincipal()); - while (list.size() >= this.capacity) { - list.remove(0); + public synchronized List find(String principal, Date after) { + LinkedList events = new LinkedList(); + for (int i = 0; i < this.events.length; i++) { + int index = ((this.tail + this.events.length - i) % this.events.length); + AuditEvent event = this.events[index]; + if (event == null) { + break; + } + if (isMatch(event, principal, after)) { + events.addFirst(event); } - list.add(event); } + return events; + } + + private boolean isMatch(AuditEvent auditEvent, String principal, Date after) { + return (principal == null || auditEvent.getPrincipal().equals(principal)) + && (after == null || auditEvent.getTimestamp().compareTo(after) >= 0); + } + + @Override + public synchronized void add(AuditEvent event) { + this.tail = (this.tail + 1) % this.events.length; + this.events[this.tail] = event; } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/audit/InMemoryAuditEventRepositoryTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/audit/InMemoryAuditEventRepositoryTests.java index 6753edfdc47..74dba50c3f8 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/audit/InMemoryAuditEventRepositoryTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/audit/InMemoryAuditEventRepositoryTests.java @@ -16,30 +16,85 @@ package org.springframework.boot.actuate.audit; +import java.util.Calendar; import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.junit.Test; -import static org.junit.Assert.assertEquals; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertThat; /** * Tests for {@link InMemoryAuditEventRepository}. * * @author Dave Syer + * @author Phillip Webb */ public class InMemoryAuditEventRepositoryTests { - private final InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository(); + @Test + public void lessThanCapacity() throws Exception { + InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository(); + repository.add(new AuditEvent("dave", "a")); + repository.add(new AuditEvent("dave", "b")); + List events = repository.find("dave", null); + assertThat(events.size(), equalTo(2)); + assertThat(events.get(0).getType(), equalTo("a")); + assertThat(events.get(1).getType(), equalTo("b")); + + } @Test - public void testAddToCapacity() throws Exception { - this.repository.setCapacity(2); - this.repository.add(new AuditEvent("phil", "UNKNOWN")); - this.repository.add(new AuditEvent("phil", "UNKNOWN")); - this.repository.add(new AuditEvent("dave", "UNKNOWN")); - this.repository.add(new AuditEvent("dave", "UNKNOWN")); - this.repository.add(new AuditEvent("phil", "UNKNOWN")); - assertEquals(2, this.repository.find("phil", new Date(0L)).size()); + public void capacity() throws Exception { + InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository(2); + repository.add(new AuditEvent("dave", "a")); + repository.add(new AuditEvent("dave", "b")); + repository.add(new AuditEvent("dave", "c")); + List events = repository.find("dave", null); + assertThat(events.size(), equalTo(2)); + assertThat(events.get(0).getType(), equalTo("b")); + assertThat(events.get(1).getType(), equalTo("c")); + } + + @Test + public void findByPrincipal() throws Exception { + InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository(); + repository.add(new AuditEvent("dave", "a")); + repository.add(new AuditEvent("phil", "b")); + repository.add(new AuditEvent("dave", "c")); + repository.add(new AuditEvent("phil", "d")); + List events = repository.find("dave", null); + assertThat(events.size(), equalTo(2)); + assertThat(events.get(0).getType(), equalTo("a")); + assertThat(events.get(1).getType(), equalTo("c")); + } + + @Test + public void findByDate() throws Exception { + Calendar calendar = Calendar.getInstance(); + calendar.set(2000, 1, 1, 0, 0, 0); + calendar.set(Calendar.MILLISECOND, 0); + Map data = new HashMap(); + InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository(); + repository.add(new AuditEvent(calendar.getTime(), "dave", "a", data)); + calendar.add(Calendar.DAY_OF_YEAR, 1); + repository.add(new AuditEvent(calendar.getTime(), "phil", "b", data)); + calendar.add(Calendar.DAY_OF_YEAR, 1); + Date after = calendar.getTime(); + repository.add(new AuditEvent(calendar.getTime(), "dave", "c", data)); + calendar.add(Calendar.DAY_OF_YEAR, 1); + repository.add(new AuditEvent(calendar.getTime(), "phil", "d", data)); + calendar.add(Calendar.DAY_OF_YEAR, 1); + List events = repository.find(null, after); + assertThat(events.size(), equalTo(2)); + assertThat(events.get(0).getType(), equalTo("c")); + assertThat(events.get(1).getType(), equalTo("d")); + events = repository.find("dave", after); + assertThat(events.size(), equalTo(1)); + assertThat(events.get(0).getType(), equalTo("c")); } }