Improve AuditEventRepository

See gh-5854
pull/5879/merge
Vedran Pavic 9 years ago committed by Stephane Nicoll
parent 6527aa0e80
commit 5f19323fbd

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -80,6 +80,7 @@ public class AuditEvent implements Serializable {
public AuditEvent(Date timestamp, String principal, String type, public AuditEvent(Date timestamp, String principal, String type,
Map<String, Object> data) { Map<String, Object> data) {
Assert.notNull(timestamp, "Timestamp must not be null"); Assert.notNull(timestamp, "Timestamp must not be null");
Assert.notNull(principal, "Principal must not be null");
Assert.notNull(type, "Type must not be null"); Assert.notNull(type, "Type must not be null");
this.timestamp = timestamp; this.timestamp = timestamp;
this.principal = principal; this.principal = principal;

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,9 +23,18 @@ import java.util.List;
* Repository for {@link AuditEvent}s. * Repository for {@link AuditEvent}s.
* *
* @author Dave Syer * @author Dave Syer
* @author Vedran Pavic
*/ */
public interface AuditEventRepository { public interface AuditEventRepository {
/**
* Find audit events since the time provided.
* @param after timestamp of earliest result required
* @return audit events
* @since 1.4.0
*/
List<AuditEvent> find(Date after);
/** /**
* Find audit events relating to the specified principal since the time provided. * Find audit events relating to the specified principal since the time provided.
* @param principal the principal name to search for * @param principal the principal name to search for
@ -34,6 +43,17 @@ public interface AuditEventRepository {
*/ */
List<AuditEvent> find(String principal, Date after); List<AuditEvent> find(String principal, Date after);
/**
* Find audit events of specified type relating to the specified principal since the
* time provided.
* @param principal the principal name to search for
* @param type the event type to search for
* @param after timestamp of earliest result required
* @return audit events of specified type relating to the principal
* @since 1.4.0
*/
List<AuditEvent> find(String principal, String type, Date after);
/** /**
* Log an event. * Log an event.
* @param event the audit event to log * @param event the audit event to log

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,11 +20,14 @@ import java.util.Date;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.springframework.util.Assert;
/** /**
* In-memory {@link AuditEventRepository} implementation. * In-memory {@link AuditEventRepository} implementation.
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb * @author Phillip Webb
* @author Vedran Pavic
*/ */
public class InMemoryAuditEventRepository implements AuditEventRepository { public class InMemoryAuditEventRepository implements AuditEventRepository {
@ -33,7 +36,7 @@ public class InMemoryAuditEventRepository implements AuditEventRepository {
/** /**
* Circular buffer of the event with tail pointing to the last element. * Circular buffer of the event with tail pointing to the last element.
*/ */
private AuditEvent[] events; private final AuditEvent[] events;
private volatile int tail = -1; private volatile int tail = -1;
@ -45,39 +48,84 @@ public class InMemoryAuditEventRepository implements AuditEventRepository {
this.events = new AuditEvent[capacity]; this.events = new AuditEvent[capacity];
} }
/** @Override
* Set the capacity of this event repository. public List<AuditEvent> find(Date after) {
* @param capacity the capacity LinkedList<AuditEvent> events = new LinkedList<AuditEvent>();
*/ synchronized (this.events) {
public synchronized void setCapacity(int capacity) { for (int i = 0; i < this.events.length; i++) {
this.events = new AuditEvent[capacity]; AuditEvent event = resolveTailEvent(i);
if (event == null) {
break;
}
if (isMatch(event, after)) {
events.addFirst(event);
}
}
}
return events;
} }
@Override @Override
public synchronized List<AuditEvent> find(String principal, Date after) { public List<AuditEvent> find(String principal, Date after) {
Assert.notNull(principal, "Principal must not be null");
LinkedList<AuditEvent> events = new LinkedList<AuditEvent>(); LinkedList<AuditEvent> events = new LinkedList<AuditEvent>();
for (int i = 0; i < this.events.length; i++) { synchronized (this.events) {
int index = ((this.tail + this.events.length - i) % this.events.length); for (int i = 0; i < this.events.length; i++) {
AuditEvent event = this.events[index]; AuditEvent event = resolveTailEvent(i);
if (event == null) { if (event == null) {
break; break;
} }
if (isMatch(event, principal, after)) { if (isMatch(event, principal, after)) {
events.addFirst(event); events.addFirst(event);
}
} }
} }
return events; return events;
} }
private boolean isMatch(AuditEvent auditEvent, String principal, Date after) { @Override
return (principal == null || auditEvent.getPrincipal().equals(principal)) public List<AuditEvent> find(String principal, String type, Date after) {
&& (after == null || auditEvent.getTimestamp().compareTo(after) >= 0); Assert.notNull(principal, "Principal must not be null");
Assert.notNull(type, "Type must not be null");
LinkedList<AuditEvent> events = new LinkedList<AuditEvent>();
synchronized (this.events) {
for (int i = 0; i < this.events.length; i++) {
AuditEvent event = resolveTailEvent(i);
if (event == null) {
break;
}
if (isMatch(event, principal, type, after)) {
events.addFirst(event);
}
}
}
return events;
} }
@Override @Override
public synchronized void add(AuditEvent event) { public void add(AuditEvent event) {
this.tail = (this.tail + 1) % this.events.length; Assert.notNull(event, "AuditEvent must not be null");
this.events[this.tail] = event; synchronized (this.events) {
this.tail = (this.tail + 1) % this.events.length;
this.events[this.tail] = event;
}
}
private AuditEvent resolveTailEvent(int offset) {
int index = ((this.tail + this.events.length - offset) % this.events.length);
return this.events[index];
}
private boolean isMatch(AuditEvent event, Date after) {
return (after == null || event.getTimestamp().compareTo(after) >= 0);
}
private boolean isMatch(AuditEvent event, String principal, Date after) {
return (event.getPrincipal().equals(principal) && isMatch(event, after));
}
private boolean isMatch(AuditEvent event, String principal, String type, Date after) {
return (event.getType().equals(type) && isMatch(event, principal, after));
} }
} }

@ -18,7 +18,9 @@ package org.springframework.boot.actuate.audit;
import java.util.Collections; import java.util.Collections;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -26,11 +28,15 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link AuditEvent}. * Tests for {@link AuditEvent}.
* *
* @author Dave Syer * @author Dave Syer
* @author Vedran Pavic
*/ */
public class AuditEventTests { public class AuditEventTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test @Test
public void testNowEvent() throws Exception { public void nowEvent() throws Exception {
AuditEvent event = new AuditEvent("phil", "UNKNOWN", AuditEvent event = new AuditEvent("phil", "UNKNOWN",
Collections.singletonMap("a", (Object) "b")); Collections.singletonMap("a", (Object) "b"));
assertThat(event.getData().get("a")).isEqualTo("b"); assertThat(event.getData().get("a")).isEqualTo("b");
@ -40,10 +46,32 @@ public class AuditEventTests {
} }
@Test @Test
public void testConvertStringsToData() throws Exception { public void convertStringsToData() throws Exception {
AuditEvent event = new AuditEvent("phil", "UNKNOWN", "a=b", "c=d"); AuditEvent event = new AuditEvent("phil", "UNKNOWN", "a=b", "c=d");
assertThat(event.getData().get("a")).isEqualTo("b"); assertThat(event.getData().get("a")).isEqualTo("b");
assertThat(event.getData().get("c")).isEqualTo("d"); assertThat(event.getData().get("c")).isEqualTo("d");
} }
@Test
public void nullTimestamp() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Timestamp must not be null");
new AuditEvent(null, "phil", "UNKNOWN",
Collections.singletonMap("a", (Object) "b"));
}
@Test
public void nullPrincipal() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Principal must not be null");
new AuditEvent(null, "UNKNOWN", Collections.singletonMap("a", (Object) "b"));
}
@Test
public void nullType() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Type must not be null");
new AuditEvent("phil", null, Collections.singletonMap("a", (Object) "b"));
}
} }

@ -22,7 +22,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -31,9 +33,13 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb * @author Phillip Webb
* @author Vedran Pavic
*/ */
public class InMemoryAuditEventRepositoryTests { public class InMemoryAuditEventRepositoryTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test @Test
public void lessThanCapacity() throws Exception { public void lessThanCapacity() throws Exception {
InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository(); InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository();
@ -43,7 +49,6 @@ public class InMemoryAuditEventRepositoryTests {
assertThat(events.size()).isEqualTo(2); assertThat(events.size()).isEqualTo(2);
assertThat(events.get(0).getType()).isEqualTo("a"); assertThat(events.get(0).getType()).isEqualTo("a");
assertThat(events.get(1).getType()).isEqualTo("b"); assertThat(events.get(1).getType()).isEqualTo("b");
} }
@Test @Test
@ -58,6 +63,14 @@ public class InMemoryAuditEventRepositoryTests {
assertThat(events.get(1).getType()).isEqualTo("c"); assertThat(events.get(1).getType()).isEqualTo("c");
} }
@Test
public void addNullAuditEvent() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("AuditEvent must not be null");
InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository();
repository.add(null);
}
@Test @Test
public void findByPrincipal() throws Exception { public void findByPrincipal() throws Exception {
InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository(); InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository();
@ -71,6 +84,19 @@ public class InMemoryAuditEventRepositoryTests {
assertThat(events.get(1).getType()).isEqualTo("c"); assertThat(events.get(1).getType()).isEqualTo("c");
} }
@Test
public void findByPrincipalAndType() 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<AuditEvent> events = repository.find("dave", "a", null);
assertThat(events.size()).isEqualTo(1);
assertThat(events.get(0).getPrincipal()).isEqualTo("dave");
assertThat(events.get(0).getType()).isEqualTo("a");
}
@Test @Test
public void findByDate() throws Exception { public void findByDate() throws Exception {
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
@ -87,7 +113,7 @@ public class InMemoryAuditEventRepositoryTests {
calendar.add(Calendar.DAY_OF_YEAR, 1); calendar.add(Calendar.DAY_OF_YEAR, 1);
repository.add(new AuditEvent(calendar.getTime(), "phil", "d", data)); repository.add(new AuditEvent(calendar.getTime(), "phil", "d", data));
calendar.add(Calendar.DAY_OF_YEAR, 1); calendar.add(Calendar.DAY_OF_YEAR, 1);
List<AuditEvent> events = repository.find(null, after); List<AuditEvent> events = repository.find(after);
assertThat(events.size()).isEqualTo(2); assertThat(events.size()).isEqualTo(2);
assertThat(events.get(0).getType()).isEqualTo("c"); assertThat(events.get(0).getType()).isEqualTo("c");
assertThat(events.get(1).getType()).isEqualTo("d"); assertThat(events.get(1).getType()).isEqualTo("d");

Loading…
Cancel
Save