[15x] Remove Read Auditing feature

This commit is contained in:
Rob Bygrave
2023-10-10 23:45:07 +13:00
parent 9373275ffa
commit 8f89b19128
33 changed files with 1 additions and 1448 deletions
@@ -670,16 +670,6 @@ public interface ExpressionList<T> {
*/
Query<T> setDisableLazyLoading(boolean disableLazyLoading);
/**
* Disable read auditing for this query.
* <p>
* This is intended to be used when the query is not a user initiated query and instead
* part of the internal processing in an application to load a cache or document store etc.
* In these cases we don't want the query to be part of read auditing.
* </p>
*/
Query<T> setDisableReadAuditing();
/**
* Set a label on the query (to help identify query execution statistics).
*/
@@ -424,16 +424,6 @@ public interface Query<T> extends CancelableQuery {
*/
Query<T> setIncludeSoftDeletes();
/**
* Disable read auditing for this query.
* <p>
* This is intended to be used when the query is not a user initiated query and instead
* part of the internal processing in an application to load a cache or document store etc.
* In these cases we don't want the query to be part of read auditing.
* </p>
*/
Query<T> setDisableReadAuditing();
/**
* Specify the properties to fetch on the root level entity bean in comma delimited format.
* <p>
@@ -14,8 +14,6 @@ import io.ebean.event.*;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetricNamingMatch;
import io.ebean.util.StringHelper;
@@ -416,8 +414,6 @@ public class DatabaseConfig {
private ChangeLogListener changeLogListener;
private ChangeLogRegister changeLogRegister;
private boolean changeLogAsync = true;
private ReadAuditLogger readAuditLogger;
private ReadAuditPrepare readAuditPrepare;
private EncryptKeyManager encryptKeyManager;
private EncryptDeployManager encryptDeployManager;
private Encryptor encryptor;
@@ -1197,40 +1193,6 @@ public class DatabaseConfig {
this.changeLogAsync = changeLogAsync;
}
/**
* Return the ReadAuditLogger to use.
*/
public ReadAuditLogger getReadAuditLogger() {
return readAuditLogger;
}
/**
* Set the ReadAuditLogger to use. If not set the default implementation is used
* which logs the read events in JSON format to a standard named SLF4J logger
* (which can be configured in say logback to log to a separate log file).
*/
public void setReadAuditLogger(ReadAuditLogger readAuditLogger) {
this.readAuditLogger = readAuditLogger;
}
/**
* Return the ReadAuditPrepare to use.
*/
public ReadAuditPrepare getReadAuditPrepare() {
return readAuditPrepare;
}
/**
* Set the ReadAuditPrepare to use.
* <p>
* It is expected that an implementation is used that read user context information
* (user id, user ip address etc) and sets it on the ReadEvent bean before it is sent
* to the ReadAuditLogger.
*/
public void setReadAuditPrepare(ReadAuditPrepare readAuditPrepare) {
this.readAuditPrepare = readAuditPrepare;
}
/**
* Return the configuration for profiling.
*/
@@ -1,37 +0,0 @@
package io.ebean.event.readaudit;
/**
* Log that the query was executed
*/
public interface ReadAuditLogger {
/**
* Called when a new query plan is created.
* <p>
* The query plan has the full sql and logging the query plan separately means that each of
* the bean and many read events can log the query plan key and not the full sql (reducing the
* bulk size of the read audit logs).
* </p>
*/
void queryPlan(ReadAuditQueryPlan queryPlan);
/**
* Audit a find bean query that returned a bean.
* <p>
* Finds that did not return a bean are excluded.
* </p>
*/
void auditBean(ReadEvent readBean);
/**
* Audit a find many query that returned some beans.
* <p>
* Finds that did not return any beans are excluded.
* </p>
* <p>
* For large queries executed via findEach() etc the ids are collected in batches
* and logged. Hence the ids list has a maximum size of the batch size.
* </p>
*/
void auditMany(ReadEvent readMany);
}
@@ -1,23 +0,0 @@
package io.ebean.event.readaudit;
/**
* Set user context information into the read event prior to it being logged.
*/
public interface ReadAuditPrepare {
/**
* Prepare the read event by setting any user context information into the read event such as the
* application user id and ip address.
* <p>
* This method is called prior to the read event being sent to the ReadAuditLogger.
* </p>
* <p>
* Note that for findFutureList() queries prepare() is called early in the foreground thread
* prior to the query executing and at that point the ReadEvent bean only has the bean type
* and no other details (which are populated later when the query is executed in the background
* thread).
* </p>
*/
void prepare(ReadEvent readEvent);
}
@@ -1,97 +0,0 @@
package io.ebean.event.readaudit;
/**
* A SQL query and associated keys.
* <p>
* This is logged as a separate event so that the
* </p>
*/
public class ReadAuditQueryPlan {
String beanType;
String queryKey;
String sql;
/**
* Construct given the beanType, queryKey and sql.
*/
public ReadAuditQueryPlan(String beanType, String queryKey, String sql) {
this.beanType = beanType;
this.queryKey = queryKey;
this.sql = sql;
}
/**
* Construct for JSON tools.
*/
public ReadAuditQueryPlan() {
}
@Override
public String toString() {
return "beanType:" + beanType + " queryKey:" + queryKey + " sql:" + sql;
}
/**
* Return the bean type.
*/
public String getBeanType() {
return beanType;
}
/**
* Set the bean type.
*/
public void setBeanType(String beanType) {
this.beanType = beanType;
}
/**
* Return the query key (relative to the bean type).
*/
public String getQueryKey() {
return queryKey;
}
/**
* Set the query key.
*/
public void setQueryKey(String queryKey) {
this.queryKey = queryKey;
}
/**
* Return the sql statement.
*/
public String getSql() {
return sql;
}
/**
* Set the sql statement.
*/
public void setSql(String sql) {
this.sql = sql;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ReadAuditQueryPlan that = (ReadAuditQueryPlan) o;
if (!beanType.equals(that.beanType)) return false;
if (!queryKey.equals(that.queryKey)) return false;
return sql.equals(that.sql);
}
@Override
public int hashCode() {
int result = beanType.hashCode();
result = 92821 * result + queryKey.hashCode();
result = 92821 * result + sql.hashCode();
return result;
}
}
@@ -1,261 +0,0 @@
package io.ebean.event.readaudit;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Read event sent to the ReadEventLogger.
* <p>
* This is a flattened in that it contains either a read bean or list of beans. It is flattened
* in this way to simplify logging and processing and simply means that it either contains an
* id or a list of ids.
* </p>
*/
public class ReadEvent {
/**
* User defined 'source' such as the application name.
*/
protected String source;
/**
* Application user id expected to be optionally populated by ChangeLogPrepare.
*/
protected String userId;
/**
* Application user ip address expected to be optionally populated by ChangeLogPrepare.
*/
protected String userIpAddress;
/**
* Arbitrary user context information expected to be optionally populated by ChangeLogPrepare.
*/
protected Map<String, String> userContext;
/**
* The time the bean change was created.
*/
protected long eventTime;
/**
* The type of the bean(s) read.
*/
protected String beanType;
/**
* The query key (relative to the bean type).
*/
protected String queryKey;
/**
* The bind log when the query was executed.
*/
protected String bindLog;
/**
* The id of the bean read.
*/
protected Object id;
/**
* The ids of the beans read.
*/
protected List<Object> ids;
/**
* Common constructor for single bean and multi-bean read events.
*/
protected ReadEvent(String beanType, String queryKey, String bindLog) {
this.beanType = beanType;
this.queryKey = queryKey;
this.bindLog = bindLog;
this.eventTime = System.currentTimeMillis();
}
/**
* Construct for a single bean read.
*/
public ReadEvent(String beanType, String queryKey, String bindLog, Object id) {
this(beanType, queryKey, bindLog);
this.id = id;
}
/**
* Construct for many beans read.
*/
public ReadEvent(String beanType, String queryKey, String bindLog, List<Object> ids) {
this(beanType, queryKey, bindLog);
this.ids = ids;
}
/**
* Construct for many future list query.
*/
public ReadEvent(String beanType) {
this.beanType = beanType;
this.eventTime = System.currentTimeMillis();
}
/**
* Constructor for JSON tools.
*/
public ReadEvent() {
}
/**
* Return a code that identifies the source of the change (like the name of the application).
*/
public String getSource() {
return source;
}
/**
* Set the source of the change (like the name of the application).
*/
public void setSource(String source) {
this.source = source;
}
/**
* Return the application user Id.
*/
public String getUserId() {
return userId;
}
/**
* Set the application user Id.
* <p>
* This can be set by the ChangeLogListener in the prepare() method which is called
* in the foreground thread.
* </p>
*/
public void setUserId(String userId) {
this.userId = userId;
}
/**
* Return the application users ip address.
*/
public String getUserIpAddress() {
return userIpAddress;
}
/**
* Set the application users ip address.
* <p>
* This can be set by the ChangeLogListener in the prepare() method which is called
* in the foreground thread.
* </p>
*/
public void setUserIpAddress(String userIpAddress) {
this.userIpAddress = userIpAddress;
}
/**
* Return a user context value - anything you set yourself in ChangeLogListener prepare().
*/
public Map<String, String> getUserContext() {
if (userContext == null) {
userContext = new LinkedHashMap<>();
}
return userContext;
}
/**
* Set a user context value (anything you like).
* <p>
* This can be set by the ChangeLogListener in the prepare() method which is called
* in the foreground thread.
* </p>
*/
public void setUserContext(Map<String, String> userContext) {
this.userContext = userContext;
}
/**
* Return the type of bean read.
*/
public String getBeanType() {
return beanType;
}
/**
* Set the type of bean read.
*/
public void setBeanType(String beanType) {
this.beanType = beanType;
}
/**
* Return the query key (relative to the bean type).
*/
public String getQueryKey() {
return queryKey;
}
/**
* Set the query key (relative to the bean type).
*/
public void setQueryKey(String queryKey) {
this.queryKey = queryKey;
}
/**
* Return the bind log used when executing the query.
*/
public String getBindLog() {
return bindLog;
}
/**
* Set the bind log used when executing the query.
*/
public void setBindLog(String bindLog) {
this.bindLog = bindLog;
}
/**
* Return the event date time.
*/
public long getEventTime() {
return eventTime;
}
/**
* Set the event date time.
*/
public void setEventTime(long eventTime) {
this.eventTime = eventTime;
}
/**
* Return the id of the bean read.
*/
public Object getId() {
return id;
}
/**
* Set the id of the bean read.
*/
public void setId(Object id) {
this.id = id;
}
/**
* Return the ids of the beans read.
*/
public List<Object> getIds() {
return ids;
}
/**
* Set the ids of the beans read.
*/
public void setIds(List<Object> ids) {
this.ids = ids;
}
}
@@ -1,8 +0,0 @@
/**
* Provides Auditing of read events including queries and L2 cache.
* <p>
* Provides a built support for supplied an audit of all the 'read events' for beans annotated
* with <code>@ReadAudit</code>
* </p>
*/
package io.ebean.event.readaudit;
-1
View File
@@ -30,7 +30,6 @@ module io.ebean.api {
exports io.ebean.config;
exports io.ebean.config.dbplatform;
exports io.ebean.event;
exports io.ebean.event.readaudit;
exports io.ebean.event.changelog;
exports io.ebean.common;
exports io.ebean.docstore;
@@ -4,8 +4,6 @@ import io.avaje.lang.Nullable;
import io.ebean.*;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.CallOrigin;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetricVisitor;
import io.ebean.plugin.SpiServer;
import io.ebeaninternal.api.SpiQuery.Type;
@@ -190,17 +188,6 @@ public interface SpiEbeanServer extends SpiServer, ExtendedServer, BeanCollectio
*/
boolean isSupportedType(java.lang.reflect.Type genericType);
/**
* Return the ReadAuditLogger to use for logging all read audit events.
*/
ReadAuditLogger readAuditLogger();
/**
* Return the ReadAuditPrepare used to populate the read audit events with
* user context information (user id, user ip address etc).
*/
ReadAuditPrepare readAuditPrepare();
/**
* Return the DataTimeZone to use when reading/writing timestamps via JDBC.
*/
@@ -10,7 +10,6 @@ import io.ebean.Query;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.plugin.BeanType;
import io.ebeaninternal.server.autotune.ProfilingListener;
import io.ebeaninternal.server.core.SpiOrmQueryRequest;
@@ -867,21 +866,6 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
*/
int bufferFetchSizeHint();
/**
* Return true if read auditing is disabled on this query.
*/
boolean isDisableReadAudit();
/**
* Set the readEvent for future queries (as prepared in foreground thread).
*/
void setFutureFetchAudit(ReadEvent event);
/**
* Read the readEvent for future queries (null otherwise).
*/
ReadEvent futureFetchAudit();
/**
* Return the base table to use if user defined on the query.
*/
@@ -13,8 +13,6 @@ import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.BeanPersistController;
import io.ebean.event.ShutdownManager;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.*;
import io.ebean.migration.auto.AutoMigrationRunner;
import io.ebean.plugin.BeanType;
@@ -92,8 +90,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final DtoBeanManager dtoBeanManager;
private final BeanDescriptorManager descriptorManager;
private final AutoTuneService autoTuneService;
private final ReadAuditPrepare readAuditPrepare;
private final ReadAuditLogger readAuditLogger;
private final CQueryEngine cqueryEngine;
private final List<Plugin> serverPlugins;
private final SpiDdlGenerator ddlGenerator;
@@ -145,8 +141,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.relationalQueryEngine = config.createRelationalQueryEngine();
this.dtoQueryEngine = config.createDtoQueryEngine();
this.autoTuneService = config.createAutoTuneService(this);
this.readAuditPrepare = config.getReadAuditPrepare();
this.readAuditLogger = config.getReadAuditLogger();
this.beanLoader = new DefaultBeanLoader(this);
this.jsonContext = config.createJsonContext(this);
this.dataTimeZone = config.getDataTimeZone();
@@ -282,16 +276,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return transactionManager.readOnlyDataSource();
}
@Override
public ReadAuditPrepare readAuditPrepare() {
return readAuditPrepare;
}
@Override
public ReadAuditLogger readAuditLogger() {
return readAuditLogger;
}
/**
* Run any initialisation required before registering with the ClusterManager.
*/
@@ -1276,10 +1260,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
SpiQuery<T> spiQuery = query.copy();
// FutureList query always run in it's own persistence content
spiQuery.setPersistenceContext(new DefaultPersistenceContext());
if (!spiQuery.isDisableReadAudit()) {
BeanDescriptor<T> desc = descriptorManager.descriptor(spiQuery.getBeanType());
desc.readAuditFutureList(spiQuery);
}
// Create a new transaction solely to execute the findList() at some future time
boolean createdTransaction = false;
SpiTransaction transaction = query.transaction();
@@ -13,8 +13,6 @@ import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.plugin.Plugin;
import io.ebean.plugin.SpiServer;
import io.ebeaninternal.api.*;
@@ -44,8 +42,6 @@ import io.ebeaninternal.server.persist.DefaultPersister;
import io.ebeaninternal.server.persist.platform.MultiValueBind;
import io.ebeaninternal.server.persist.platform.PostgresMultiValueBind;
import io.ebeaninternal.server.query.*;
import io.ebeaninternal.server.readaudit.DefaultReadAuditLogger;
import io.ebeaninternal.server.readaudit.DefaultReadAuditPrepare;
import io.ebeaninternal.server.json.DJsonContext;
import io.ebeaninternal.server.transaction.*;
import io.ebeaninternal.server.type.DefaultTypeManager;
@@ -231,22 +227,6 @@ public final class InternalConfiguration {
return plugin((listener != null) ? listener : jacksonCorePresent ? new DefaultChangeLogListener() : null);
}
/**
* Return the ReadAuditLogger implementation to use.
*/
ReadAuditLogger getReadAuditLogger() {
ReadAuditLogger found = bootupClasses.getReadAuditLogger();
return plugin(found != null ? found : jacksonCorePresent ? new DefaultReadAuditLogger() : null);
}
/**
* Return the ReadAuditPrepare implementation to use.
*/
ReadAuditPrepare getReadAuditPrepare() {
ReadAuditPrepare found = bootupClasses.getReadAuditPrepare();
return plugin(found != null ? found : new DefaultReadAuditPrepare());
}
/**
* For 'As Of' queries return the number of bind variables per predicate.
*/
@@ -648,17 +648,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
}
Object cached = beanDescriptor.queryCacheGet(cacheKey);
if (cached != null && isAuditReads() && readAuditQueryType()) {
if (cached instanceof BeanCollection) {
// raw sql can't use L2 cache so normal queries only in here
Collection<T> actualDetails = ((BeanCollection<T>) cached).actualDetails();
List<Object> ids = new ArrayList<>(actualDetails.size());
for (T bean : actualDetails) {
ids.add(beanDescriptor.idForJson(bean));
}
beanDescriptor.readAuditMany(queryPlanKey.partialKey(), "l2-query-cache", ids);
}
}
if (Boolean.FALSE.equals(query.isReadOnly())) {
// return shallow copies if readonly is explicitly set to false
if (cached instanceof BeanCollection) {
@@ -674,24 +663,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
return cached;
}
/**
* Return true if the query type contains bean data (not just ids etc) and hence we want to include
* it in read auditing. Return false for row count and find ids queries.
*/
private boolean readAuditQueryType() {
Type type = query.type();
switch (type) {
case BEAN:
case ITERATE:
case LIST:
case SET:
case MAP:
return true;
default:
return false;
}
}
public void putToQueryCache(Object result) {
beanDescriptor.queryCachePut(cacheKey, new QueryCacheEntry(result, dependentTables, transaction.startNanoTime()));
}
@@ -718,15 +689,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
return (batchSize > 0) ? batchSize : server.lazyLoadBatchSize();
}
/**
* Return true if read auditing is on for this query request.
* <p>
* This means that read audit is on for this bean type and that query has not explicitly disabled it.
*/
public boolean isAuditReads() {
return beanDescriptor.isReadAuditing() && !query.isDisableReadAudit();
}
/**
* Return the base table alias for this query.
*/
@@ -9,8 +9,6 @@ import io.ebean.event.*;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.api.CoreLog;
@@ -68,14 +66,10 @@ public class BootupClasses implements Predicate<Class<?>> {
private Class<? extends ChangeLogPrepare> changeLogPrepareClass;
private Class<? extends ChangeLogListener> changeLogListenerClass;
private Class<? extends ChangeLogRegister> changeLogRegisterClass;
private Class<? extends ReadAuditPrepare> readAuditPrepareClass;
private Class<? extends ReadAuditLogger> readAuditLoggerClass;
private ChangeLogPrepare changeLogPrepare;
private ChangeLogListener changeLogListener;
private ChangeLogRegister changeLogRegister;
private ReadAuditPrepare readAuditPrepare;
private ReadAuditLogger readAuditLogger;
public BootupClasses() {
}
@@ -173,19 +167,11 @@ public class BootupClasses implements Predicate<Class<?>> {
}
public void addChangeLogInstances(DatabaseConfig config) {
readAuditPrepare = config.getReadAuditPrepare();
readAuditLogger = config.getReadAuditLogger();
changeLogPrepare = config.getChangeLogPrepare();
changeLogListener = config.getChangeLogListener();
changeLogRegister = config.getChangeLogRegister();
// if not already set create the implementations found
// via classpath scanning
if (readAuditPrepare == null && readAuditPrepareClass != null) {
readAuditPrepare = create(readAuditPrepareClass, false);
}
if (readAuditLogger == null && readAuditLoggerClass != null) {
readAuditLogger = create(readAuditLoggerClass, false);
}
if (changeLogPrepare == null && changeLogPrepareClass != null) {
changeLogPrepare = create(changeLogPrepareClass, false);
}
@@ -252,14 +238,6 @@ public class BootupClasses implements Predicate<Class<?>> {
return changeLogRegister;
}
public ReadAuditPrepare getReadAuditPrepare() {
return readAuditPrepare;
}
public ReadAuditLogger getReadAuditLogger() {
return readAuditLogger;
}
public List<IdGenerator> getIdGenerators() {
return createAdd(idGeneratorInstances, idGeneratorCandidates);
}
@@ -424,15 +402,6 @@ public class BootupClasses implements Predicate<Class<?>> {
interesting = true;
}
if (ReadAuditPrepare.class.isAssignableFrom(cls)) {
readAuditPrepareClass = (Class<? extends ReadAuditPrepare>) cls;
interesting = true;
}
if (ReadAuditLogger.class.isAssignableFrom(cls)) {
readAuditLoggerClass = (Class<? extends ReadAuditLogger>) cls;
interesting = true;
}
return interesting;
}
@@ -14,9 +14,6 @@ import io.ebean.event.*;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeLogFilter;
import io.ebean.event.changelog.ChangeType;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.meta.MetaQueryPlan;
import io.ebean.meta.MetricVisitor;
import io.ebean.meta.QueryPlanInit;
@@ -123,7 +120,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
private final TablespaceMeta tablespaceMeta;
private final String storageEngine;
private final String dbComment;
private final boolean readAuditing;
private final boolean draftable;
private final boolean draftableElement;
private final BeanProperty unmappedJson;
@@ -260,7 +256,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
this.selectLastInsertedIdDraft = deploy.getSelectLastInsertedIdDraft();
this.concurrencyMode = deploy.getConcurrencyMode();
this.indexDefinitions = deploy.getIndexDefinitions();
this.readAuditing = deploy.isReadAuditing();
this.draftable = deploy.isDraftable();
this.draftableElement = deploy.isDraftableElement();
this.historySupport = deploy.isHistorySupport();
@@ -719,20 +714,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
}
}
/**
* Return the ReadAuditLogger for logging read audit events.
*/
public ReadAuditLogger readAuditLogger() {
return ebeanServer.readAuditLogger();
}
/**
* Return the ReadAuditPrepare for preparing read audit events prior to logging.
*/
private ReadAuditPrepare readAuditPrepare() {
return ebeanServer.readAuditPrepare();
}
public boolean isChangeLog() {
return changeLogFilter != null;
}
@@ -1354,52 +1335,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
cacheHelp.cacheBeanUpdate(key, changes, updateNaturalKey, version);
}
/**
* Prepare the read audit of a findFutureList() query.
*/
public void readAuditFutureList(SpiQuery<T> spiQuery) {
if (isReadAuditing()) {
ReadEvent event = new ReadEvent(fullName);
// prepare in the foreground thread while we have the user context
// information (query is processed/executed later in bg thread)
readAuditPrepare(event);
spiQuery.setFutureFetchAudit(event);
}
}
/**
* Write a bean read to the read audit log.
*/
public void readAuditBean(String queryKey, String bindLog, Object bean) {
ReadEvent event = new ReadEvent(fullName, queryKey, bindLog, idForJson(bean));
readAuditPrepare(event);
readAuditLogger().auditBean(event);
}
private void readAuditPrepare(ReadEvent event) {
ReadAuditPrepare prepare = readAuditPrepare();
if (prepare != null) {
prepare.prepare(event);
}
}
/**
* Write a many bean read to the read audit log.
*/
public void readAuditMany(String queryKey, String bindLog, List<Object> ids) {
ReadEvent event = new ReadEvent(fullName, queryKey, bindLog, ids);
readAuditPrepare(event);
readAuditLogger().auditMany(event);
}
/**
* Write a futureList many read to the read audit log.
*/
public void readAuditFutureMany(ReadEvent event) {
// this has already been prepared (in foreground thread)
readAuditLogger().auditMany(event);
}
/**
* Return the base table alias. This is always the first letter of the bean name.
*/
@@ -1777,9 +1712,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
if (d != null) {
Object shareableBean = d.getSharableBean();
if (shareableBean != null) {
if (isReadAuditing()) {
readAuditBean("ref", "", shareableBean);
}
return (T) shareableBean;
}
}
@@ -2708,13 +2640,6 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
return draftTable;
}
/**
* Return true if read auditing is on this entity bean.
*/
public boolean isReadAuditing() {
return readAuditing;
}
@Override
public boolean isSoftDelete() {
return softDelete;
@@ -594,9 +594,6 @@ final class BeanDescriptorCacheHelp<T> {
if (beanLog.isLoggable(TRACE)) {
beanLog.log(TRACE, " GET {0}({1}) - hit shared bean", cacheName, id);
}
if (desc.isReadAuditing()) {
desc.readAuditBean("l2", "", bean);
}
return (T) bean;
}
}
@@ -648,9 +645,6 @@ final class BeanDescriptorCacheHelp<T> {
}
CachedBeanDataToBean.load(desc, bean, data, context);
if (desc.isReadAuditing()) {
desc.readAuditBean("l2", "", bean);
}
return bean;
}
@@ -20,7 +20,6 @@ import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.idgen.UuidV1IdGenerator;
import io.ebeaninternal.server.idgen.UuidV1RndIdGenerator;
import io.ebeaninternal.server.idgen.UuidV4IdGenerator;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import jakarta.persistence.Entity;
import jakarta.persistence.MappedSuperclass;
@@ -31,10 +30,6 @@ import java.util.*;
*/
public class DeployBeanDescriptor<T> {
private static final Map<String, String> EMPTY_NAMED_QUERY = new HashMap<>();
private static final Map<String, SpiRawSql> EMPTY_RAW_MAP = new HashMap<>();
private static class PropOrder implements Comparator<DeployBeanProperty> {
@Override
@@ -84,7 +79,6 @@ public class DeployBeanDescriptor<T> {
private String draftTable;
private String[] dependentTables;
private boolean historySupport;
private boolean readAuditing;
private boolean draftable;
private boolean draftableElement;
private TableName baseTableFull;
@@ -193,20 +187,6 @@ public class DeployBeanDescriptor<T> {
return historySupport;
}
/**
* Set read auditing on for this entity bean.
*/
public void setReadAuditing() {
readAuditing = true;
}
/**
* Return true if read auditing is on for this entity bean.
*/
public boolean isReadAuditing() {
return readAuditing;
}
public void setDbComment(String dbComment) {
this.dbComment = dbComment;
}
@@ -164,7 +164,7 @@ final class AnnotationClass extends AnnotationParser {
}
ReadAudit readAudit = typeGet(cls, ReadAudit.class);
if (readAudit != null) {
descriptor.setReadAuditing();
// TODO: Not supported
}
History history = typeGet(cls, History.class);
if (history != null) {
@@ -536,11 +536,6 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
return query.setDisableLazyLoading(disableLazyLoading);
}
@Override
public Query<T> setDisableReadAuditing() {
return query.setDisableReadAuditing();
}
@Override
public Query<T> setLabel(String label) {
return query.setLabel(label);
@@ -995,11 +995,6 @@ final class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expr
return exprList.setDisableLazyLoading(disableLazyLoading);
}
@Override
public Query<T> setDisableReadAuditing() {
return exprList.setDisableReadAuditing();
}
@Override
public Query<T> setCountDistinct(CountDistinctOrder orderBy) {
return exprList.setCountDistinct(orderBy);
@@ -31,7 +31,6 @@ public final class DLoadContext implements LoadContext {
private final CacheMode useBeanCache;
private final int defaultBatchSize;
private final boolean disableLazyLoading;
private final boolean disableReadAudit;
private final boolean includeSoftDeletes;
final boolean useDocStore;
@@ -64,7 +63,6 @@ public final class DLoadContext implements LoadContext {
this.asOf = null;
this.readOnly = false;
this.disableLazyLoading = false;
this.disableReadAudit = false;
this.includeSoftDeletes = false;
this.relativePath = null;
this.planLabel = null;
@@ -91,7 +89,6 @@ public final class DLoadContext implements LoadContext {
this.asDraft = query.isAsDraft();
this.includeSoftDeletes = query.isIncludeSoftDeletes() && query.mode() == SpiQuery.Mode.NORMAL;
this.readOnly = query.isReadOnly();
this.disableReadAudit = query.isDisableReadAudit();
this.disableLazyLoading = query.isDisableLazyLoading();
this.useBeanCache = query.beanCacheMode();
this.profilingListener = query.profilingListener();
@@ -331,9 +328,6 @@ public final class DLoadContext implements LoadContext {
if (includeSoftDeletes) {
query.setIncludeSoftDeletes();
}
if (disableReadAudit) {
query.setDisableReadAuditing();
}
if (profilingListener != null) {
query.setProfilingListener(profilingListener);
}
@@ -5,7 +5,6 @@ import io.ebean.QueryIterator;
import io.ebean.Version;
import io.ebean.bean.*;
import io.ebean.core.type.DataReader;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.util.JdbcClose;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.api.SpiProfileTransactionEvent;
@@ -159,28 +158,12 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
private long executionTimeMicros;
/**
* Flag set when read auditing.
*/
private final boolean audit;
/**
* Flag set when findIterate is being read audited meaning we log in batches.
*/
private boolean auditFindIterate;
/**
* A buffer of Ids collected for findIterate auditing.
*/
private List<Object> auditIds;
/**
* Create the Sql select based on the request.
*/
@SuppressWarnings("unchecked")
public CQuery(OrmQueryRequest<T> request, CQueryPredicates predicates, CQueryPlan queryPlan) {
this.request = request;
this.audit = request.isAuditReads();
this.queryPlan = queryPlan;
this.query = request.query();
this.queryMode = query.mode();
@@ -349,13 +332,6 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
* The JDBC resultSet and statement need to be closed. Its important that this method is called.
*/
public void close() {
try {
if (auditFindIterate && auditIds != null && !auditIds.isEmpty()) {
auditIterateLogMessage();
}
} catch (Throwable e) {
CoreLog.log.log(ERROR, "Error logging read audit logs", e);
}
try {
if (dataReader != null) {
dataReader.close();
@@ -475,9 +451,6 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
}
EntityBean next() {
if (audit) {
auditNextBean();
}
hasNextCache = false;
if (nextBean == null) {
throw new NoSuchElementException();
@@ -682,66 +655,6 @@ public final class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfi
this.currentPathMap = currentPathMap;
}
/**
* A find bean query with read auditing so build and log the ReadEvent.
*/
void auditFind(EntityBean bean) {
if (bean != null) {
// only audit when a bean was actually found
desc.readAuditBean(queryPlan.auditQueryKey(), bindLog, bean);
}
}
/**
* a find many query with read auditing so build the ReadEvent and log it.
*/
void auditFindMany() {
if (auditIds != null && !auditIds.isEmpty()) {
// get the id values of the underlying collection
ReadEvent futureReadEvent = query.futureFetchAudit();
if (futureReadEvent == null) {
// normal query execution
desc.readAuditMany(queryPlan.auditQueryKey(), bindLog, auditIds);
} else {
// this query was executed via findFutureList() and the prepare()
// has already been called so set the details and log
futureReadEvent.setQueryKey(queryPlan.auditQueryKey());
futureReadEvent.setBindLog(bindLog);
futureReadEvent.setIds(auditIds);
desc.readAuditFutureMany(futureReadEvent);
}
}
}
/**
* Indicate that read auditing is occurring on this findIterate query.
*/
void auditFindIterate() {
auditFindIterate = true;
}
/**
* Send the current buffer of findIterate collected ids to the audit log.
*/
private void auditIterateLogMessage() {
desc.readAuditMany(queryPlan.auditQueryKey(), bindLog, auditIds);
// create a new list on demand with the next bean/id
auditIds = null;
}
/**
* Add the id to the audit id buffer and flush if needed in batches of 100.
*/
private void auditNextBean() {
if (auditIds == null) {
auditIds = new ArrayList<>(100);
}
auditIds.add(desc.idForJson(nextBean));
if (auditFindIterate && auditIds.size() >= 100) {
auditIterateLogMessage();
}
}
/**
* Return the underlying PreparedStatement.
*/
@@ -9,7 +9,6 @@ import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.SqlLimitRequest;
import io.ebean.config.dbplatform.SqlLimitResponse;
import io.ebean.config.dbplatform.SqlLimiter;
import io.ebean.event.readaudit.ReadAuditQueryPlan;
import io.ebean.text.PathProperties;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiQuery;
@@ -345,11 +344,6 @@ final class CQueryBuilder {
} else {
queryPlan = new CQueryPlan(request, res, sqlTree.plan(), false, predicates.logWhereSql());
}
BeanDescriptor<T> desc = request.descriptor();
if (desc.isReadAuditing()) {
// log the query plan based bean type (i.e. ignoring query disabling for logging the sql/plan)
desc.readAuditLogger().queryPlan(new ReadAuditQueryPlan(desc.fullName(), queryPlan.auditQueryKey(), queryPlan.sql()));
}
// cache the query plan because we can reuse it and also
// gather query performance statistics based on it.
request.putQueryPlan(queryPlan);
@@ -207,10 +207,6 @@ public final class CQueryEngine {
if (request.logSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
// indicates we need to audit as the iterator progresses
cquery.auditFindIterate();
}
return readIterate;
} catch (SQLException e) {
@@ -253,9 +249,6 @@ public final class CQueryEngine {
if (request.logSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFindMany();
}
return versions;
} catch (SQLException e) {
@@ -348,9 +341,6 @@ public final class CQueryEngine {
if (request.logSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFindMany();
}
request.executeSecondaryQueries(false);
if (request.isQueryCachePut()) {
request.addDependentTables(cquery.dependentTables());
@@ -383,9 +373,6 @@ public final class CQueryEngine {
if (request.logSummary()) {
logFindBeanSummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFind(bean);
}
request.executeSecondaryQueries(false);
return (T) bean;
} catch (SQLException e) {
@@ -210,11 +210,6 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> setDisableReadAuditing() {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
}
@Override
public Query<T> apply(FetchPath fetchPath) {
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
@@ -7,7 +7,6 @@ import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.BeanQueryRequest;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.plugin.BeanType;
import io.ebeaninternal.api.*;
import io.ebeaninternal.server.autotune.ProfilingListener;
@@ -86,11 +85,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
*/
private boolean distinct;
/**
* Only used for read auditing with findFutureList() query.
*/
private ReadEvent futureFetchAudit;
private int timeout;
/**
@@ -1899,27 +1893,6 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
return bufferFetchSizeHint;
}
@Override
public final Query<T> setDisableReadAuditing() {
this.disableReadAudit = true;
return this;
}
@Override
public final boolean isDisableReadAudit() {
return disableReadAudit;
}
@Override
public final void setFutureFetchAudit(ReadEvent event) {
this.futureFetchAudit = event;
}
@Override
public final ReadEvent futureFetchAudit() {
return futureFetchAudit;
}
@Override
public final Query<T> setBaseTable(String baseTable) {
this.baseTable = baseTable;
@@ -1,128 +0,0 @@
package io.ebeaninternal.server.readaudit;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import io.avaje.applog.AppLog;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditQueryPlan;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.text.json.EJson;
import io.ebeaninternal.api.CoreLog;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Map;
import static java.lang.System.Logger.Level.ERROR;
import static java.lang.System.Logger.Level.INFO;
/**
* Default implementation of ReadAuditLogger that writes the event in JSON format to standard loggers.
*/
public class DefaultReadAuditLogger implements ReadAuditLogger {
private static final System.Logger queryLogger = AppLog.getLogger("io.ebean.ReadAuditQuery");
private static final System.Logger auditLogger = AppLog.getLogger("io.ebean.ReadAudit");
protected final JsonFactory jsonFactory = new JsonFactory();
protected final int defaultQueryBuffer = 500;
protected final int defaultReadBuffer = 150;
/**
* Write the query plan details in JSON format to the logger.
*/
@Override
public void queryPlan(ReadAuditQueryPlan queryPlan) {
StringWriter writer = new StringWriter(defaultQueryBuffer);
try (JsonGenerator gen = jsonFactory.createGenerator(writer)) {
gen.writeStartObject();
String beanType = queryPlan.getBeanType();
if (beanType != null) {
gen.writeStringField("beanType", beanType);
}
String queryKey = queryPlan.getQueryKey();
if (queryKey != null) {
gen.writeStringField("queryKey", queryKey);
}
String sql = queryPlan.getSql();
if (sql != null) {
gen.writeStringField("sql", sql);
}
gen.writeEndObject();
gen.flush();
queryLogger.log(INFO, writer.toString());
} catch (IOException e) {
CoreLog.log.log(ERROR, "Error writing Read audit event", e);
}
}
/**
* Write the bean read event details in JSON format to the logger.
*/
@Override
public void auditBean(ReadEvent beanEvent) {
writeEvent(beanEvent);
}
/**
* Write the many beans read event details in JSON format to the logger.
*/
@Override
public void auditMany(ReadEvent readMany) {
writeEvent(readMany);
}
protected void writeEvent(ReadEvent event) {
try {
StringWriter writer = new StringWriter(defaultReadBuffer);
JsonGenerator gen = jsonFactory.createGenerator(writer);
writeDetails(gen, event);
auditLogger.log(INFO, writer.toString());
} catch (IOException e) {
CoreLog.log.log(ERROR, "Error writing Read audit event", e);
}
}
/**
* Write the details for the read bean or read many beans event.
*/
protected void writeDetails(JsonGenerator gen, ReadEvent event) throws IOException {
gen.writeStartObject();
String source = event.getSource();
if (source != null) {
gen.writeStringField("source", source);
}
String userId = event.getUserId();
if (userId != null) {
gen.writeStringField("userId", userId);
}
String userIpAddress = event.getUserIpAddress();
if (userIpAddress != null) {
gen.writeStringField("userIpAddress", userIpAddress);
}
Map<String, String> userContext = event.getUserContext();
if (userContext != null && !userContext.isEmpty()) {
gen.writeObjectFieldStart("userContext");
for (Map.Entry<String, String> entry : userContext.entrySet()) {
gen.writeStringField(entry.getKey(), entry.getValue());
}
gen.writeEndObject();
}
gen.writeNumberField("eventTime", event.getEventTime());
gen.writeStringField("beanType", event.getBeanType());
gen.writeStringField("queryKey", event.getQueryKey());
gen.writeStringField("bindLog", event.getBindLog());
Object id = event.getId();
if (id != null) {
gen.writeFieldName("id");
EJson.write(id, gen);
} else {
gen.writeFieldName("ids");
EJson.writeCollection(event.getIds(), gen);
}
gen.writeEndObject();
gen.flush();
gen.close();
}
}
@@ -1,19 +0,0 @@
package io.ebeaninternal.server.readaudit;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.event.readaudit.ReadEvent;
/**
* A placeholder implementation for ReadAuditPrepare.
* <p>
* A real application specific implementation is required to obtain and set
* the user context information on the readEvent bean (like user id).
* </p>
*/
public final class DefaultReadAuditPrepare implements ReadAuditPrepare {
@Override
public void prepare(ReadEvent readEvent) {
// do nothing by default.
}
}
@@ -1048,19 +1048,6 @@ public abstract class TQRootBean<T, R> {
return root;
}
/**
* Disable read auditing for this query.
* <p>
* This is intended to be used when the query is not a user initiated query and instead
* part of the internal processing in an application to load a cache or document store etc.
* In these cases we don't want the query to be part of read auditing.
* </p>
*/
public R setDisableReadAuditing() {
query.setDisableReadAuditing();
return root;
}
/**
* Set this to true to use the query cache.
*/
@@ -1,22 +0,0 @@
package io.ebean.xtest.event.readaudit;
import io.ebean.event.readaudit.ReadAuditQueryPlan;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class ReadAuditQueryPlanTest {
@Test
public void testEquals() {
ReadAuditQueryPlan plan1 = new ReadAuditQueryPlan("org.Bean", "queryKey", "select id from foo");
assertEquals(plan1, new ReadAuditQueryPlan("org.Bean", "queryKey", "select id from foo"));
assertNotEquals(plan1, new ReadAuditQueryPlan("org.Bean", "queryKey", "select id from Notfoo"));
assertNotEquals(plan1, new ReadAuditQueryPlan("org.Bean", "notQueryKey", "select id from foo"));
assertNotEquals(plan1, new ReadAuditQueryPlan("org.NotBean", "queryKey", "select id from foo"));
}
}
@@ -9,8 +9,6 @@ import io.ebean.bean.CallOrigin;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
import io.ebean.meta.MetricVisitor;
import io.ebean.plugin.Property;
@@ -150,16 +148,6 @@ public class TDSpiEbeanServer extends TDSpiServer implements SpiEbeanServer {
return null;
}
@Override
public ReadAuditLogger readAuditLogger() {
return null;
}
@Override
public ReadAuditPrepare readAuditPrepare() {
return null;
}
@Override
public void clearQueryStatistics() {
@@ -1,375 +0,0 @@
package org.tests.readaudit;
import io.ebean.xtest.BaseTestCase;
import io.ebean.Database;
import io.ebean.DatabaseFactory;
import io.ebean.FutureList;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheStatistics;
import io.ebean.config.DatabaseConfig;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.event.readaudit.ReadAuditQueryPlan;
import io.ebean.event.readaudit.ReadEvent;
import io.ebean.test.LoggedSql;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Country;
import org.tests.model.basic.EBasicChangeLog;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
public class TestReadAudit extends BaseTestCase {
TDReadAuditPrepare readAuditPrepare = new TDReadAuditPrepare();
TDReadAuditLogger readAuditLogger = new TDReadAuditLogger();
Database server;
Long id1;
Long id2;
@BeforeEach
public void setup() {
server = createServer();
EBasicChangeLog bean = new EBasicChangeLog();
bean.setName("readAudito1");
bean.setShortDescription("readAudit hello");
server.save(bean);
id1 = bean.getId();
EBasicChangeLog bean2 = new EBasicChangeLog();
bean2.setName("readAudito2");
bean2.setShortDescription("readAudit hi");
server.save(bean2);
id2 = bean2.getId();
Country ar = new Country();
ar.setCode("AR");
ar.setName("Argentina");
server.save(ar);
}
@AfterEach
public void shutdown() {
server.shutdown();
}
@Test
public void test_findById() {
resetCounters();
EBasicChangeLog found = server.find(EBasicChangeLog.class, id1);
assertThat(found).isNotNull();
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.beans).hasSize(1);
assertThat(readAuditLogger.beans.get(0).getBeanType()).isEqualTo(EBasicChangeLog.class.getName());
assertThat(readAuditLogger.beans.get(0).getId()).isEqualTo(id1);
}
@Test
public void test_findById_usingL2Cache() {
resetCounters();
ServerCache beanCache = server.cacheManager().beanCache(EBasicChangeLog.class);
beanCache.statistics(true);
EBasicChangeLog found = server.find(EBasicChangeLog.class, id1);
assertThat(found).isNotNull();
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.beans).hasSize(1);
assertThat(readAuditLogger.beans.get(0).getBeanType()).isEqualTo(EBasicChangeLog.class.getName());
assertThat(readAuditLogger.beans.get(0).getId()).isEqualTo(id1);
ServerCacheStatistics statistics = beanCache.statistics(false);
assertThat(statistics.getSize()).isEqualTo(1);
assertThat(statistics.getHitCount()).isEqualTo(0);
EBasicChangeLog found2 = server.find(EBasicChangeLog.class, id1);
assertThat(found2).isNotNull();
statistics = beanCache.statistics(false);
assertThat(statistics.getSize()).isEqualTo(1);
assertThat(statistics.getHitCount()).isEqualTo(1);
assertThat(readAuditLogger.beans).hasSize(2);
}
@Test
public void test_findById_usingL2Cache_sharedBean() {
resetCounters();
ServerCache beanCache = server.cacheManager().beanCache(Country.class);
beanCache.statistics(true);
Country found = server.find(Country.class, "AR");
assertThat(found).isNotNull();
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.beans).hasSize(1);
assertThat(readAuditLogger.beans.get(0).getBeanType()).isEqualTo(Country.class.getName());
assertThat(readAuditLogger.beans.get(0).getId()).isEqualTo("AR");
ServerCacheStatistics statistics = beanCache.statistics(false);
assertThat(statistics.getSize()).isEqualTo(1);
assertThat(statistics.getHitCount()).isEqualTo(0);
Country found2 = server.find(Country.class, "AR");
assertThat(found2).isNotNull();
statistics = beanCache.statistics(false);
assertThat(statistics.getSize()).isEqualTo(1);
assertThat(statistics.getHitCount()).isEqualTo(1);
assertThat(readAuditLogger.beans).hasSize(2);
Country ref = server.reference(Country.class, "AR");
assertThat(readAuditLogger.beans).hasSize(3);
assertThat(ref).isSameAs(found2);
}
@Test
public void test_findList() {
resetCounters();
List<EBasicChangeLog> list = server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findList();
assertThat(list).hasSize(2);
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(1);
assertThat(readAuditLogger.many.get(0).getBeanType()).isEqualTo(EBasicChangeLog.class.getName());
assertThat(readAuditLogger.many.get(0).getIds()).contains(id1, id2);
server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findList();
assertThat(readAuditPrepare.count).isEqualTo(2);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(2);
}
@Test
public void test_findList_useL2Cache() {
resetCounters();
LoggedSql.start();
List<EBasicChangeLog> list = server.find(EBasicChangeLog.class)
.setUseQueryCache(true)
.where().startsWith("shortDescription", "readAudit")
.findList();
System.out.println("test_findList_useL2Cache> first query");
assertThat(list).hasSize(2);
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(1);
assertThat(readAuditLogger.many.get(0).getBeanType()).isEqualTo(EBasicChangeLog.class.getName());
assertThat(readAuditLogger.many.get(0).getIds()).contains(id1, id2);
List<EBasicChangeLog> list1 = server.find(EBasicChangeLog.class)
.setUseQueryCache(true)
.where().startsWith("shortDescription", "readAudit")
.findList();
System.out.println("test_findList_useL2Cache> second query " + list1.size());
assertThat(list1).hasSize(2);
List<String> sql = LoggedSql.stop();
System.out.println("test_findList_useL2Cache> sql: " + sql);
//assertThat(sql).hasSize(1);
System.out.println("test_findList_useL2Cache> prepare:" + readAuditPrepare.count
+ " plans:" + readAuditLogger.plans
+ " many:" + readAuditLogger.many);
assertThat(readAuditPrepare.count).isEqualTo(2);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(2);
}
@Test
public void test_findFutureList() throws ExecutionException, InterruptedException {
resetCounters();
FutureList<EBasicChangeLog> futureList = server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findFutureList();
List<EBasicChangeLog> list = futureList.get();
assertThat(list).hasSize(2);
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(1);
assertThat(readAuditLogger.many.get(0).getBeanType()).isEqualTo(EBasicChangeLog.class.getName());
assertThat(readAuditLogger.many.get(0).getIds()).contains(id1, id2);
server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findList();
assertThat(readAuditPrepare.count).isEqualTo(2);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(2);
}
@Test
public void test_findSet() {
resetCounters();
Set<EBasicChangeLog> list = server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findSet();
assertThat(list).hasSize(2);
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(1);
assertThat(readAuditLogger.many.get(0).getIds()).contains(id1, id2);
server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findSet();
assertThat(readAuditPrepare.count).isEqualTo(2);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(2);
}
@Test
public void test_findMap() {
resetCounters();
Map<Long, EBasicChangeLog> list = server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findMap();
assertThat(list).hasSize(2);
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(1);
assertThat(readAuditLogger.many.get(0).getIds()).contains(id1, id2);
server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findMap();
assertThat(readAuditPrepare.count).isEqualTo(2);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(2);
}
@Test
public void test_findEach() {
resetCounters();
final AtomicInteger count = new AtomicInteger();
server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findEach(bean -> count.incrementAndGet());
assertThat(count.get()).isEqualTo(2);
assertThat(readAuditPrepare.count).isEqualTo(1);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(1);
assertThat(readAuditLogger.many.get(0).getIds()).contains(id1, id2);
server.find(EBasicChangeLog.class)
.where().startsWith("shortDescription", "readAudit")
.findEach(bean -> count.incrementAndGet());
assertThat(readAuditPrepare.count).isEqualTo(2);
assertThat(readAuditLogger.plans).hasSize(1);
assertThat(readAuditLogger.many).hasSize(2);
}
private Database createServer() {
DatabaseConfig config = new DatabaseConfig();
config.setName("h2other");
config.loadFromProperties();
config.setDdlGenerate(true);
config.setDdlRun(true);
config.setDdlExtra(false);
config.setDefaultServer(false);
config.setRegister(false);
config.addClass(Country.class);
config.addClass(EBasicChangeLog.class);
config.setReadAuditLogger(readAuditLogger);
config.setReadAuditPrepare(readAuditPrepare);
return DatabaseFactory.create(config);
}
private void resetCounters() {
readAuditLogger.resetCounters();
readAuditPrepare.resetCounters();
}
static class TDReadAuditPrepare implements ReadAuditPrepare {
int count;
void resetCounters() {
count = 0;
}
@Override
public void prepare(ReadEvent event) {
count++;
event.setUserId("appUser1");
event.setUserIpAddress("1.1.1.1");
event.getUserContext().put("some", "thing");
}
}
static class TDReadAuditLogger implements ReadAuditLogger {
Set<ReadAuditQueryPlan> plans = new HashSet<>();
List<ReadEvent> beans = new ArrayList<>();
List<ReadEvent> many = new ArrayList<>();
void resetCounters() {
plans.clear();
beans.clear();
many.clear();
}
@Override
public void queryPlan(ReadAuditQueryPlan queryPlan) {
plans.add(queryPlan);
}
@Override
public void auditBean(ReadEvent readBean) {
beans.add(readBean);
}
@Override
public void auditMany(ReadEvent readMany) {
many.add(readMany);
}
}
}