001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.actuate.audit; 018 019import java.time.Instant; 020import java.util.LinkedList; 021import java.util.List; 022 023import org.springframework.util.Assert; 024 025/** 026 * In-memory {@link AuditEventRepository} implementation. 027 * 028 * @author Dave Syer 029 * @author Phillip Webb 030 * @author Vedran Pavic 031 */ 032public class InMemoryAuditEventRepository implements AuditEventRepository { 033 034 private static final int DEFAULT_CAPACITY = 1000; 035 036 private final Object monitor = new Object(); 037 038 /** 039 * Circular buffer of the event with tail pointing to the last element. 040 */ 041 private AuditEvent[] events; 042 043 private volatile int tail = -1; 044 045 public InMemoryAuditEventRepository() { 046 this(DEFAULT_CAPACITY); 047 } 048 049 public InMemoryAuditEventRepository(int capacity) { 050 this.events = new AuditEvent[capacity]; 051 } 052 053 /** 054 * Set the capacity of this event repository. 055 * @param capacity the capacity 056 */ 057 public void setCapacity(int capacity) { 058 synchronized (this.monitor) { 059 this.events = new AuditEvent[capacity]; 060 } 061 } 062 063 @Override 064 public void add(AuditEvent event) { 065 Assert.notNull(event, "AuditEvent must not be null"); 066 synchronized (this.monitor) { 067 this.tail = (this.tail + 1) % this.events.length; 068 this.events[this.tail] = event; 069 } 070 } 071 072 @Override 073 public List<AuditEvent> find(String principal, Instant after, String type) { 074 LinkedList<AuditEvent> events = new LinkedList<>(); 075 synchronized (this.monitor) { 076 for (int i = 0; i < this.events.length; i++) { 077 AuditEvent event = resolveTailEvent(i); 078 if (event != null && isMatch(principal, after, type, event)) { 079 events.addFirst(event); 080 } 081 } 082 } 083 return events; 084 } 085 086 private boolean isMatch(String principal, Instant after, String type, 087 AuditEvent event) { 088 boolean match = true; 089 match = match && (principal == null || event.getPrincipal().equals(principal)); 090 match = match && (after == null || event.getTimestamp().isAfter(after)); 091 match = match && (type == null || event.getType().equals(type)); 092 return match; 093 } 094 095 private AuditEvent resolveTailEvent(int offset) { 096 int index = ((this.tail + this.events.length - offset) % this.events.length); 097 return this.events[index]; 098 } 099 100}