com.avaje.ebean.DelegateEbeanServer Maven / Gradle / Ivy
package com.avaje.ebean;
import com.avaje.ebean.backgroundexecutor.ImmediateBackgroundExecutor;
import com.avaje.ebean.cache.ServerCacheManager;
import com.avaje.ebean.delegate.DelegateBulkUpdate;
import com.avaje.ebean.delegate.DelegateDelete;
import com.avaje.ebean.delegate.DelegateFind;
import com.avaje.ebean.delegate.DelegateFindSqlQuery;
import com.avaje.ebean.delegate.DelegatePublish;
import com.avaje.ebean.delegate.DelegateQuery;
import com.avaje.ebean.delegate.DelegateSave;
import com.avaje.ebean.delegate.InterceptBulkUpdate;
import com.avaje.ebean.delegate.InterceptDelete;
import com.avaje.ebean.delegate.InterceptFind;
import com.avaje.ebean.delegate.InterceptFindSqlQuery;
import com.avaje.ebean.delegate.InterceptPublish;
import com.avaje.ebean.delegate.InterceptSave;
import com.avaje.ebean.meta.MetaInfoManager;
import com.avaje.ebean.plugin.SpiServer;
import com.avaje.ebean.text.csv.CsvReader;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebeaninternal.api.SpiQuery;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Wraps an underlying EbeanServer.
*
* Can you used for testing purposes when you want to create a test double that
* only replaces some of the underlying functionality of the EbeanServer, for example
* only overwrites some of the find or save functionality but leaves the rest of the
* behavior to be handled normally by the underlying delegate.
*
* The underlying delegate is most often a fully functional EbeanServer that is using H2
* as a test database.
*
*/
public class DelegateEbeanServer implements EbeanServer, DelegateAwareEbeanServer, DelegateMethodNames {
/**
* The list of methods calls made to this server.
*/
public MethodCalls methodCalls = new MethodCalls();
/**
* The beans sent to the save(), delete() methods etc.
*/
public BeanCapture capturedBeans = new BeanCapture();
/**
* Various find methods that have specific test responses.
*/
protected WhenFind whenFind = new WhenFind();
/**
* Test double replacements for 'Finders' which are static fields on entity beans.
*/
protected WithStaticFinders withStaticFinders = new WithStaticFinders();
/**
* The underlying EbeanServer we delegate to.
*
* This will often be a fully functional EbeanSever that uses H2.
*/
protected EbeanServer delegate;
/**
* Expect ImmediateBackgroundExecutor to be a good default. Can use IgnoreBackgroundExecutor or the delegates one.
*/
protected BackgroundExecutor backgroundExecutor = new ImmediateBackgroundExecutor();
/**
* Constructs queries that will call back to this so not really expecting it to be overwritten.
*/
protected DelegateQuery delegateQuery;
protected InterceptSave save;
protected InterceptBulkUpdate bulkUpdate;
protected InterceptDelete delete;
protected InterceptFind find;
protected InterceptPublish publish;
protected InterceptFindSqlQuery findSqlQuery;
/**
* If set to true the 'bulk update' calls are passed through to the underlying delegate.
*/
protected boolean persistBulkUpdates;
/**
* If set to true the 'delete' calls are passed through to the underlying delegate.
*/
protected boolean persistDeletes;
/**
* If set to true the 'insert' calls are passed through to the underlying delegate.
*/
protected boolean persistInserts;
/**
* If set to true the 'save' calls are passed through to the underlying delegate.
*/
protected boolean persistSaves;
/**
* If set to true the 'update' calls are passed through to the underlying delegate.
*/
protected boolean persistUpdates;
/**
* If set to true the publish/draft calls are passed through to the underlying delegate.
*/
protected boolean persistPublish;
/**
* Construct with defaults.
*/
public DelegateEbeanServer() {
}
/**
* Construct with a EbeanServer to delegate and using ImmediateBackgroundExecutor.
*
* This delegate will be used on all method calls that are not overwritten.
*/
public DelegateEbeanServer withDelegate(EbeanServer delegate) {
this.delegate = delegate;
this.delegateQuery = new DelegateQuery(delegate, this);
this.save = new DelegateSave(delegate);
this.delete = new DelegateDelete(delegate);
this.bulkUpdate = new DelegateBulkUpdate(delegate);
this.find = new DelegateFind(delegate);
this.findSqlQuery = new DelegateFindSqlQuery(delegate);
this.publish = new DelegatePublish(delegate);
return this;
}
@Override
public void beforeRun() {
withStaticFinders.beforeRun();
}
@Override
public void afterRun() {
withStaticFinders.afterRun();
}
public WhenFind whenFind() {
return whenFind;
}
/**
* Used to specify a test double to replace a static 'finder' field on the given beanType.
*
{@code
*
* DelegateEbeanServer mock = new DelegateEbeanServer();
* mock.withFinder(Customer.class).as(new TDCustomerFinder());
*
* // Note: TDCustomerFinder not set onto Customer until runWithMock()
*
* MockiEbean.runWithMock(mock, new Runnable() {
*
* public void run() {
* ...
* // Customer.find now is our test double TDCustomerFinder
* Customer found = Customer.find.byUniqueName("foo");
* }
* });
*
* // Note: original Customer.find implementation is restored by MockiEbean
*
* }
*/
public WithStaticFinder withFinder(Class beanType) {
return withStaticFinders.withFinder(beanType);
}
/**
* Set the underlying delegate to proxy requests to.
*
* Used with the default constructor such that this DelegateEbeanServer
* can be setup prior to having access to the underlying EbeanServer
* that we want to proxy through to.
*
* Return true if the underling ebeanServer was set.
*/
public boolean withDelegateIfRequired(EbeanServer delegate) {
if (this.delegate == null) {
withDelegate(delegate);
return true;
}
if (this.delegate instanceof DelegateAwareEbeanServer) {
// probably using ProxyEbeanServer to capture method calls etc
return ((DelegateAwareEbeanServer)this.delegate).withDelegateIfRequired(delegate);
}
// delegate was not set
return false;
}
public DelegateEbeanServer withInterceptFind(InterceptFind find) {
this.find = find;
return this;
}
/**
* Set to true for all the persisting calls skip/avoid calling the underlying delegate.
*
* So when set to true then all the calls to save(), delete() etc do not get passed on the
* the underlying delegate.
*/
public DelegateEbeanServer withPersisting(boolean persisting) {
persistBulkUpdates = persisting;
persistDeletes = persisting;
persistInserts = persisting;
persistUpdates = persisting;
persistSaves = persisting;
return this;
}
@Override
public SpiServer getPluginApi() {
methodCalls.add(MethodCall.of("getPluginApi"));
return delegate.getPluginApi();
}
@Override
public AutoTune getAutoTune() {
methodCalls.add(MethodCall.of("getAutoTune"));
return delegate.getAutoTune();
}
@Override
public DocumentStore docStore() {
methodCalls.add(MethodCall.of("docStore"));
return delegate.docStore();
}
/**
* Return the BackgroundExecutor.
*
* Typically for testing we either want these to run immediately or not at all.
* Defaults to use ImmediateBackgroundExecutor, use IgnoreBackgroundExecutor if desired.
*/
@Override
public BackgroundExecutor getBackgroundExecutor() {
methodCalls.add(MethodCall.of("getBackgroundExecutor"));
return backgroundExecutor;
}
@Override
public ServerCacheManager getServerCacheManager() {
methodCalls.add(MethodCall.of("getServerCacheManager"));
return delegate.getServerCacheManager();
}
@Override
public void shutdown(boolean shutdownDataSource, boolean deregisterDriver) {
methodCalls.add(MethodCall.of("shutdown").with("shutdownDataSource", shutdownDataSource, "deregisterDriver", deregisterDriver));
delegate.shutdown(shutdownDataSource, deregisterDriver);
}
@Override
public JsonContext json() {
methodCalls.add(MethodCall.of("json"));
return delegate.json();
}
@Override
public String getName() {
methodCalls.add(MethodCall.of("getName"));
return delegate.getName();
}
@Override
public ExpressionFactory getExpressionFactory() {
methodCalls.add(MethodCall.of("getExpressionFactory"));
return delegate.getExpressionFactory();
}
@Override
public MetaInfoManager getMetaInfoManager() {
methodCalls.add(MethodCall.of("getMetaInfoManager"));
return delegate.getMetaInfoManager();
}
@Override
public BeanState getBeanState(Object bean) {
methodCalls.add(MethodCall.of("getBeanState").with("bean", bean));
return delegate.getBeanState(bean);
}
@Override
public Object getBeanId(Object bean) {
methodCalls.add(MethodCall.of("getBeanId").with("bean", bean));
return delegate.getBeanId(bean);
}
@Override
public Object setBeanId(Object bean, Object id) {
methodCalls.add(MethodCall.of("setBeanId").with("bean", bean).with("id", id));
return delegate.setBeanId(bean, id);
}
@Override
public Map diff(Object a, Object b) {
methodCalls.add(MethodCall.of("diff").with("a", a, "b", b));
return delegate.diff(a, b);
}
@Override
public T createEntityBean(Class beanType) {
methodCalls.add(MethodCall.of("createEntityBean").with("beanType", beanType));
return delegate.createEntityBean(beanType);
}
@Override
public CsvReader createCsvReader(Class beanType) {
methodCalls.add(MethodCall.of("createCsvReader").with("beanType", beanType));
return delegate.createCsvReader(beanType);
}
@Override
public Filter filter(Class beanType) {
methodCalls.add(MethodCall.of("filter").with("beanType", beanType));
return delegate.filter(beanType);
}
@Override
public void sort(List list, String sortByClause) {
methodCalls.add(MethodCall.of("sort").with("list", list, "sortByClause", sortByClause));
delegate.sort(list, sortByClause);
}
@Override
public void markAsDirty(Object bean) {
methodCalls.add(MethodCall.of("markAsDirty").with("bean", bean));
delegate.markAsDirty(bean);
}
// -- create updates ------------------------
@Override
public Update createUpdate(Class beanType, String ormUpdate) {
methodCalls.add(MethodCall.of("createUpdate").with("beanType", beanType, "ormUpdate", ormUpdate));
return delegate.createUpdate(beanType, ormUpdate);
}
@Override
public SqlUpdate createSqlUpdate(String sql) {
methodCalls.add(MethodCall.of("createSqlUpdate").with("sql", sql));
return delegate.createSqlUpdate(sql);
}
@Override
public CallableSql createCallableSql(String callableSql) {
methodCalls.add(MethodCall.of("createCallableSql").with("callableSql", callableSql));
return delegate.createCallableSql(callableSql);
}
// -- transaction ------------------------
@Override
public void execute(TxScope scope, TxRunnable runnable) {
methodCalls.add(MethodCall.of("bulkUpdate").with("scope", scope, "runnable", runnable));
delegate.execute(scope, runnable);
}
@Override
public void execute(TxRunnable runnable) {
methodCalls.add(MethodCall.of("bulkUpdate").with("runnable", runnable));
delegate.execute(runnable);
}
@Override
public T execute(TxScope scope, TxCallable callable) {
methodCalls.add(MethodCall.of("bulkUpdate").with("scope", scope, "callable", callable));
return delegate.execute(scope, callable);
}
@Override
public T execute(TxCallable callable) {
methodCalls.add(MethodCall.of("bulkUpdate").with("callable", callable));
return delegate.execute(callable);
}
@Override
public void register(TransactionCallback transactionCallback) throws PersistenceException {
methodCalls.add(MethodCall.of("register").with("transactionCallback", transactionCallback));
delegate.register(transactionCallback);
}
@Override
public Transaction createTransaction() {
methodCalls.add(MethodCall.of("createTransaction"));
return delegate.createTransaction();
}
@Override
public Transaction createTransaction(TxIsolation isolation) {
methodCalls.add(MethodCall.of("createTransaction").with("isolation", isolation));
return delegate.createTransaction(isolation);
}
@Override
public Transaction beginTransaction() {
methodCalls.add(MethodCall.of("beginTransaction"));
return delegate.beginTransaction();
}
@Override
public Transaction beginTransaction(TxIsolation isolation) {
methodCalls.add(MethodCall.of("beginTransaction").with("isolation", isolation));
return delegate.beginTransaction(isolation);
}
@Override
public Transaction beginTransaction(TxScope scope) {
methodCalls.add(MethodCall.of("beginTransaction").with("scope", scope));
return delegate.beginTransaction(scope);
}
@Override
public Transaction currentTransaction() {
methodCalls.add(MethodCall.of("currentTransaction"));
return delegate.currentTransaction();
}
@Override
public void commitTransaction() {
methodCalls.add(MethodCall.of("commitTransaction"));
delegate.commitTransaction();
}
@Override
public void rollbackTransaction() {
methodCalls.add(MethodCall.of("rollbackTransaction"));
delegate.rollbackTransaction();
}
@Override
public void endTransaction() {
methodCalls.add(MethodCall.of("endTransaction"));
delegate.endTransaction();
}
// -- delegateQuery ------------------------
@Override
public T getReference(Class beanType, Object id) {
methodCalls.add(MethodCall.of("getReference").with("beanType", beanType, "id", id));
return delegateQuery.getReference(beanType, id);
}
@Override
public Query createQuery(Class beanType) {
methodCalls.add(MethodCall.of("createQuery").with("beanType", beanType));
return delegateQuery.createQuery(beanType);
}
@Override
public Set validateQuery(Query query) {
methodCalls.add(MethodCall.of("validateQuery").with("query", query));
return delegateQuery.validateQuery(query);
}
@Override
public Query find(Class beanType) {
methodCalls.add(MethodCall.of("find").with("beanType", beanType));
return delegateQuery.find(beanType);
}
@Override
public SqlQuery createSqlQuery(String sql) {
methodCalls.add(MethodCall.of("createSqlQuery").with("sql", sql));
return delegateQuery.createSqlQuery(sql);
}
// -- refresh ------------------------
@Override
public void refresh(Object bean) {
methodCalls.add(MethodCall.of("refresh").with("bean", bean));
find.refresh(bean);
}
@Override
public void refreshMany(Object bean, String propertyName) {
methodCalls.add(MethodCall.of("refreshMany").with("bean", bean, "propertyName", propertyName));
find.refreshMany(bean, propertyName);
}
// -- find ------------------------
@Override
public T find(Class beanType, Object id) {
methodCalls.add(MethodCall.of("find").with("beanType", beanType, "id", id));
WhenBeanReturn match = whenFind.findMatchById(beanType, id);
if (match != null) {
return (T)match.val();
}
return find.find(beanType, id, null);
}
@Override
public T find(Class beanType, Object id, Transaction transaction) {
methodCalls.add(MethodCall.of("find").with("beanType", beanType, "id", id, "transaction", transaction));
WhenBeanReturn match = whenFind.findMatchById(beanType, id);
if (match != null) {
return (T)match.val();
}
return find.find(beanType, id, transaction);
}
@Override
public T findUnique(Query query, Transaction transaction) {
methodCalls.add(MethodCall.of("findUnique").with("query", query, "transaction", transaction));
WhenBeanReturn match = whenFind.findMatchByUnique(((SpiQuery)query).getBeanType());
if (match != null) {
return (T)match.val();
}
return find.findUnique(query, transaction);
}
@Override
public int findRowCount(Query query, Transaction transaction) {
methodCalls.add(MethodCall.of("findRowCount").with("query", query, "transaction", transaction));
return find.findRowCount(query, transaction);
}
@Override
public List