killbill-memoizeit
account: invoice: payment: initial pagination implementation Signed-off-by: …
Changes
osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java 7(+4 -3)
osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java 36(+34 -2)
osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java 51(+49 -2)
payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java 9(+6 -3)
pom.xml 2(+1 -1)
Details
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index 96d6191..cd2c53e 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -19,8 +19,6 @@ package com.ning.billing.account.api.user;
import java.util.List;
import java.util.UUID;
-import javax.annotation.Nullable;
-
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
@@ -36,10 +34,13 @@ import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.CallContextFactory;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
import com.google.inject.Inject;
public class DefaultAccountUserApi implements AccountUserApi {
@@ -90,25 +91,29 @@ public class DefaultAccountUserApi implements AccountUserApi {
}
@Override
- public List<Account> searchAccounts(final String searchKey, final TenantContext context) {
- final List<AccountModelDao> accountModelDaos = accountDao.searchAccounts(searchKey, internalCallContextFactory.createInternalTenantContext(context));
- return ImmutableList.<Account>copyOf(Collections2.transform(accountModelDaos, new Function<AccountModelDao, Account>() {
- @Override
- public Account apply(final AccountModelDao input) {
- return new DefaultAccount(input);
- }
- }));
+ public Pagination<Account> searchAccounts(final String searchKey, final Long offset, final Long limit, final TenantContext context) {
+ final Pagination<AccountModelDao> accountModelDaos = accountDao.searchAccounts(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return new DefaultPagination<Account>(accountModelDaos,
+ Iterators.<AccountModelDao, Account>transform(accountModelDaos.iterator(),
+ new Function<AccountModelDao, Account>() {
+ @Override
+ public Account apply(final AccountModelDao input) {
+ return new DefaultAccount(input);
+ }
+ }));
}
@Override
- public List<Account> getAccounts(final TenantContext context) {
- final List<AccountModelDao> accountModelDaos = accountDao.get(internalCallContextFactory.createInternalTenantContext(context));
- return ImmutableList.<Account>copyOf(Collections2.transform(accountModelDaos, new Function<AccountModelDao, Account>() {
- @Override
- public Account apply(@Nullable final AccountModelDao input) {
- return new DefaultAccount(input);
- }
- }));
+ public Pagination<Account> getAccounts(final Long offset, final Long limit, final TenantContext context) {
+ final Pagination<AccountModelDao> accountModelDaos = accountDao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return new DefaultPagination<Account>(accountModelDaos,
+ Iterators.<AccountModelDao, Account>transform(accountModelDaos.iterator(),
+ new Function<AccountModelDao, Account>() {
+ @Override
+ public Account apply(final AccountModelDao input) {
+ return new DefaultAccount(input);
+ }
+ }));
}
@Override
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
index eb80519..b6ed783 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountDao.java
@@ -23,13 +23,14 @@ import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.Pagination;
import com.ning.billing.util.entity.dao.EntityDao;
public interface AccountDao extends EntityDao<AccountModelDao, Account, AccountApiException> {
public AccountModelDao getAccountByKey(String key, InternalTenantContext context);
- public List<AccountModelDao> searchAccounts(String searchKey, InternalTenantContext context);
+ public Pagination<AccountModelDao> searchAccounts(String searchKey, Long offset, Long rowCount, InternalTenantContext context);
/**
* @throws AccountApiException when externalKey is null
@@ -49,6 +50,4 @@ public interface AccountDao extends EntityDao<AccountModelDao, Account, AccountA
public void removeEmail(AccountEmailModelDao email, InternalCallContext context);
public List<AccountEmailModelDao> getEmailsByAccountId(UUID accountId, InternalTenantContext context);
-
- public AccountModelDao getByRecordId(Long recordId, InternalCallContext context);
}
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index ab38c74..4cfd258 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -16,7 +16,7 @@
package com.ning.billing.account.dao;
-import java.util.List;
+import java.util.Iterator;
import java.util.UUID;
import org.skife.jdbi.v2.sqlobject.Bind;
@@ -24,11 +24,12 @@ import org.skife.jdbi.v2.sqlobject.BindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
import org.skife.jdbi.v2.sqlobject.customizers.Define;
+import org.skife.jdbi.v2.sqlobject.customizers.FetchSize;
import com.ning.billing.account.api.Account;
-import com.ning.billing.util.audit.ChangeType;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.audit.ChangeType;
import com.ning.billing.util.entity.dao.Audited;
import com.ning.billing.util.entity.dao.EntitySqlDao;
import com.ning.billing.util.entity.dao.EntitySqlDaoStringTemplate;
@@ -41,8 +42,13 @@ public interface AccountSqlDao extends EntitySqlDao<AccountModelDao, Account> {
@BindBean final InternalTenantContext context);
@SqlQuery
- public List<AccountModelDao> searchAccounts(@Define("searchKey") final String searchKey,
- @BindBean final InternalTenantContext context);
+ // Magic value to force MySQL to stream from the database
+ // See http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html (ResultSet)
+ @FetchSize(Integer.MIN_VALUE)
+ public Iterator<AccountModelDao> searchAccounts(@Define("searchKey") final String searchKey,
+ @Bind("offset") final Long offset,
+ @Bind("rowCount") final Long rowCount,
+ @BindBean final InternalTenantContext context);
@SqlQuery
public UUID getIdFromKey(@Bind("externalKey") final String key,
diff --git a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
index b73d2a5..7c71ea2 100644
--- a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
@@ -16,6 +16,7 @@
package com.ning.billing.account.dao;
+import java.util.Iterator;
import java.util.List;
import java.util.UUID;
@@ -32,21 +33,23 @@ import com.ning.billing.account.api.user.DefaultAccountCreationEvent;
import com.ning.billing.account.api.user.DefaultAccountCreationEvent.DefaultAccountData;
import com.ning.billing.bus.api.PersistentBus;
import com.ning.billing.bus.api.PersistentBus.EventBusException;
-import com.ning.billing.util.audit.ChangeType;
-import com.ning.billing.util.cache.CacheControllerDispatcher;
import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.clock.Clock;
-import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.entity.EntityPersistenceException;
+import com.ning.billing.events.AccountChangeInternalEvent;
+import com.ning.billing.events.AccountCreationInternalEvent;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.dao.NonEntityDao;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
import com.ning.billing.util.entity.dao.EntityDaoBase;
import com.ning.billing.util.entity.dao.EntitySqlDao;
import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
-import com.ning.billing.events.AccountChangeInternalEvent;
-import com.ning.billing.events.AccountCreationInternalEvent;
import com.google.inject.Inject;
@@ -104,13 +107,18 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
}
@Override
- public List<AccountModelDao> searchAccounts(final String searchKey, final InternalTenantContext context) {
- return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<AccountModelDao>>() {
- @Override
- public List<AccountModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
- return entitySqlDaoWrapperFactory.become(AccountSqlDao.class).searchAccounts(searchKey, context);
- }
- });
+ public Pagination<AccountModelDao> searchAccounts(final String searchKey, final Long offset, final Long rowCount, final InternalTenantContext context) {
+ // We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
+ // Since we want to stream the results out, we don't want to auto-commit when this method returns.
+ final AccountSqlDao accountSqlDao = transactionalSqlDao.onDemand(AccountSqlDao.class);
+
+ // Note: the connection will be busy as we stream the results out: hence we cannot use
+ // SQL_CALC_FOUND_ROWS / FOUND_ROWS and we don't know the total number of results (in this case,
+ // performing a second time the search just to get the count is too expensive to be worth it).
+ final Long count = null;
+
+ final Iterator<AccountModelDao> results = accountSqlDao.searchAccounts(searchKey, offset, rowCount, context);
+ return new DefaultPagination<AccountModelDao>(offset, rowCount, count, results);
}
@Override
@@ -236,13 +244,4 @@ public class DefaultAccountDao extends EntityDaoBase<AccountModelDao, Account, A
});
}
- @Override
- public AccountModelDao getByRecordId(final Long recordId, final InternalCallContext context) {
- return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<AccountModelDao>() {
- @Override
- public AccountModelDao inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
- return entitySqlDaoWrapperFactory.become(AccountSqlDao.class).getByRecordId(recordId, context);
- }
- });
- }
}
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index 630f1dc..561566e 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -86,22 +86,27 @@ getAccountByKey() ::= <<
where external_key = :externalKey <AND_CHECK_TENANT()>;
>>
-searchAccounts(searchKey) ::= <<
+searchAccounts(searchKey, offset, rowCount) ::= <<
select <allTableFields()>
-from accounts
-where name like ('%<searchKey>%') <AND_CHECK_TENANT()>
-union
-select <allTableFields()>
-from accounts
-where email like ('%<searchKey>%') <AND_CHECK_TENANT()>
-union
-select <allTableFields()>
-from accounts
-where external_key like ('%<searchKey>%') <AND_CHECK_TENANT()>
-union
-select <allTableFields()>
-from accounts
-where company_name like ('%<searchKey>%') <AND_CHECK_TENANT()>
+from (
+ select <allTableFields()>
+ from accounts
+ where name like ('%<searchKey>%') <AND_CHECK_TENANT()>
+ union
+ select <allTableFields()>
+ from accounts
+ where email like ('%<searchKey>%') <AND_CHECK_TENANT()>
+ union
+ select <allTableFields()>
+ from accounts
+ where external_key like ('%<searchKey>%') <AND_CHECK_TENANT()>
+ union
+ select <allTableFields()>
+ from accounts
+ where company_name like ('%<searchKey>%') <AND_CHECK_TENANT()>
+) results
+order by results.record_id
+limit :offset, :rowCount
;
>>
diff --git a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
index b8b6161..3a3620a 100644
--- a/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/MockAccountDao.java
@@ -36,14 +36,16 @@ import com.ning.billing.account.api.user.DefaultAccountCreationEvent.DefaultAcco
import com.ning.billing.bus.api.PersistentBus;
import com.ning.billing.bus.api.PersistentBus.EventBusException;
import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.util.entity.dao.MockEntityDaoBase;
import com.ning.billing.events.AccountChangeInternalEvent;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
+import com.ning.billing.util.entity.dao.MockEntityDaoBase;
import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.inject.Inject;
public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account, AccountApiException> implements AccountDao {
@@ -79,7 +81,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
final long tenantRecordId = context == null ? InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID
: context.getTenantRecordId();
final AccountChangeInternalEvent changeEvent = new DefaultAccountChangeEvent(account.getId(), currentAccount, account,
- accountRecordId, tenantRecordId, UUID.randomUUID()
+ accountRecordId, tenantRecordId, UUID.randomUUID()
);
if (changeEvent.hasChanges()) {
try {
@@ -103,9 +105,9 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
}
@Override
- public List<AccountModelDao> searchAccounts(final String searchKey, final InternalTenantContext context) {
+ public Pagination<AccountModelDao> searchAccounts(final String searchKey, final Long offset, final Long limit, final InternalTenantContext context) {
final List<AccountModelDao> results = new LinkedList<AccountModelDao>();
- for (final AccountModelDao account : get(context)) {
+ for (final AccountModelDao account : getAll(context)) {
if ((account.getName() != null && account.getName().contains(searchKey)) ||
(account.getEmail() != null && account.getEmail().contains(searchKey)) ||
(account.getExternalKey() != null && account.getExternalKey().contains(searchKey)) ||
@@ -113,7 +115,8 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
results.add(account);
}
}
- return results;
+
+ return DefaultPagination.<AccountModelDao>build(offset, limit, results);
}
@Override
@@ -152,7 +155,7 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
@Override
public List<AccountEmailModelDao> getEmailsByAccountId(final UUID accountId, final InternalTenantContext context) {
- return ImmutableList.<AccountEmailModelDao>copyOf(Collections2.filter(accountEmailSqlDao.get(context), new Predicate<AccountEmailModelDao>() {
+ return ImmutableList.<AccountEmailModelDao>copyOf(Iterables.<AccountEmailModelDao>filter(accountEmailSqlDao.getAll(context), new Predicate<AccountEmailModelDao>() {
@Override
public boolean apply(final AccountEmailModelDao input) {
return input.getAccountId().equals(accountId);
@@ -160,8 +163,4 @@ public class MockAccountDao extends MockEntityDaoBase<AccountModelDao, Account,
}));
}
- @Override
- public AccountModelDao getByRecordId(final Long recordId, final InternalCallContext context) {
- return null;
- }
}
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
index d407232..9fd099b 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestAccountDao.java
@@ -44,17 +44,20 @@ import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.customfield.StringCustomField;
import com.ning.billing.util.customfield.dao.CustomFieldModelDao;
import com.ning.billing.util.dao.TableName;
+import com.ning.billing.util.entity.Pagination;
import com.ning.billing.util.tag.DescriptiveTag;
import com.ning.billing.util.tag.Tag;
import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
import com.ning.billing.util.tag.dao.TagModelDao;
+import com.google.common.collect.ImmutableList;
+
import static com.ning.billing.account.AccountTestUtils.checkAccountsEqual;
import static com.ning.billing.account.AccountTestUtils.createTestAccount;
public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
- @Test(groups = "slow", description="Test Account: basic DAO calls")
+ @Test(groups = "slow", description = "Test Account: basic DAO calls")
public void testBasic() throws AccountApiException {
final AccountModelDao account = createTestAccount();
accountDao.create(account, internalCallContext);
@@ -68,7 +71,8 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
checkAccountsEqual(retrievedAccount, account);
// Retrieve all
- final List<AccountModelDao> all = accountDao.get(internalCallContext);
+ final Pagination<AccountModelDao> allAccounts = accountDao.getAll(internalCallContext);
+ final List<AccountModelDao> all = ImmutableList.<AccountModelDao>copyOf(allAccounts);
Assert.assertNotNull(all);
Assert.assertEquals(all.size(), 1);
checkAccountsEqual(all.get(0), account);
@@ -80,7 +84,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
}
// Simple test to ensure long phone numbers can be stored
- @Test(groups = "slow", description="Test Account DAO: long numbers")
+ @Test(groups = "slow", description = "Test Account DAO: long numbers")
public void testLongPhoneNumber() throws AccountApiException {
final AccountModelDao account = createTestAccount("123456789012345678901234");
accountDao.create(account, internalCallContext);
@@ -90,7 +94,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
}
// Simple test to ensure excessively long phone numbers cannot be stored
- @Test(groups = "slow", description="Test Account DAO: very long numbers")
+ @Test(groups = "slow", description = "Test Account DAO: very long numbers")
public void testOverlyLongPhoneNumber() throws AccountApiException {
final AccountModelDao account = createTestAccount("12345678901234567890123456");
try {
@@ -101,7 +105,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
}
}
- @Test(groups = "slow", description="Test Account DAO: custom fields")
+ @Test(groups = "slow", description = "Test Account DAO: custom fields")
public void testCustomFields() throws CustomFieldApiException {
final UUID accountId = UUID.randomUUID();
final String fieldName = UUID.randomUUID().toString().substring(0, 4);
@@ -118,7 +122,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
Assert.assertEquals(customField.getFieldValue(), fieldValue);
}
- @Test(groups = "slow", description="Test Account DAO: tags")
+ @Test(groups = "slow", description = "Test Account DAO: tags")
public void testTags() throws TagApiException, TagDefinitionApiException {
final AccountModelDao account = createTestAccount();
final TagDefinitionModelDao tagDefinition = tagDefinitionDao.create(UUID.randomUUID().toString().substring(0, 4), UUID.randomUUID().toString(), internalCallContext);
@@ -132,7 +136,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
Assert.assertEquals(tags.get(0).getObjectType(), ObjectType.ACCOUNT);
}
- @Test(groups = "slow", description="Test Account DAO: retrieve by externalKey")
+ @Test(groups = "slow", description = "Test Account DAO: retrieve by externalKey")
public void testGetIdFromKey() throws AccountApiException {
final AccountModelDao account = createTestAccount();
accountDao.create(account, internalCallContext);
@@ -141,12 +145,12 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
Assert.assertEquals(accountId, account.getId());
}
- @Test(groups = "slow", expectedExceptions = AccountApiException.class, description="Test Account DAO: retrieve by null externalKey throws an exception")
+ @Test(groups = "slow", expectedExceptions = AccountApiException.class, description = "Test Account DAO: retrieve by null externalKey throws an exception")
public void testGetIdFromKeyForNullKey() throws AccountApiException {
accountDao.getIdFromKey(null, internalCallContext);
}
- @Test(groups = "slow", description="Test Account DAO: basic update (1)")
+ @Test(groups = "slow", description = "Test Account DAO: basic update (1)")
public void testUpdate() throws Exception {
final AccountModelDao account = createTestAccount();
accountDao.create(account, internalCallContext);
@@ -163,7 +167,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
checkAccountsEqual(retrievedAccount, updatedAccount);
}
- @Test(groups = "slow", description="Test Account DAO: payment method update")
+ @Test(groups = "slow", description = "Test Account DAO: payment method update")
public void testUpdatePaymentMethod() throws Exception {
final AccountModelDao account = createTestAccount();
accountDao.create(account, internalCallContext);
@@ -181,7 +185,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
Assert.assertNull(newAccountWithPMNull.getPaymentMethodId());
}
- @Test(groups = "slow", description="Test Account DAO: basic update (2)")
+ @Test(groups = "slow", description = "Test Account DAO: basic update (2)")
public void testShouldBeAbleToUpdateSomeFields() throws Exception {
final AccountModelDao account = createTestAccount();
accountDao.create(account, internalCallContext);
@@ -196,7 +200,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
checkAccountsEqual(retrievedAccount, newAccount);
}
- @Test(groups = "slow", description="Test Account DAO: BCD of 0")
+ @Test(groups = "slow", description = "Test Account DAO: BCD of 0")
public void testShouldBeAbleToHandleBCDOfZero() throws Exception {
final AccountModelDao account = createTestAccount(0);
accountDao.create(account, internalCallContext);
@@ -206,7 +210,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
checkAccountsEqual(retrievedAccount, account);
}
- @Test(groups = "slow", description="Test Account DAO: duplicate emails throws an exception")
+ @Test(groups = "slow", description = "Test Account DAO: duplicate emails throws an exception")
public void testHandleDuplicateEmails() throws AccountApiException {
final UUID accountId = UUID.randomUUID();
final AccountEmail email = new DefaultAccountEmail(accountId, "test@gmail.com");
@@ -224,7 +228,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
}
}
- @Test(groups = "slow", description="Test Account DAO: add and remove email")
+ @Test(groups = "slow", description = "Test Account DAO: add and remove email")
public void testAddRemoveAccountEmail() throws AccountApiException {
final UUID accountId = UUID.randomUUID();
@@ -247,7 +251,7 @@ public class TestAccountDao extends AccountTestSuiteWithEmbeddedDB {
Assert.assertEquals(accountDao.getEmailsByAccountId(accountId, internalCallContext).size(), 0);
}
- @Test(groups = "slow", description="Test Account DAO: add and remove multiple emails")
+ @Test(groups = "slow", description = "Test Account DAO: add and remove multiple emails")
public void testAddAndRemoveMultipleAccountEmails() throws AccountApiException {
final UUID accountId = UUID.randomUUID();
final String email1 = UUID.randomUUID().toString();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
index 1615ee0..0a53eb3 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/api/user/DefaultInvoiceUserApi.java
@@ -32,8 +32,11 @@ import com.ning.billing.ErrorCode;
import com.ning.billing.ObjectType;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountInternalApi;
import com.ning.billing.bus.api.PersistentBus;
import com.ning.billing.bus.api.PersistentBus.EventBusException;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.invoice.InvoiceDispatcher;
import com.ning.billing.invoice.api.Invoice;
@@ -48,20 +51,20 @@ import com.ning.billing.invoice.model.DefaultInvoice;
import com.ning.billing.invoice.model.ExternalChargeInvoiceItem;
import com.ning.billing.invoice.model.InvoiceItemFactory;
import com.ning.billing.invoice.template.HtmlInvoiceGenerator;
+import com.ning.billing.tag.TagInternalApi;
import com.ning.billing.util.api.TagApiException;
import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
-import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.util.callcontext.TenantContext;
-import com.ning.billing.account.api.AccountInternalApi;
-import com.ning.billing.tag.TagInternalApi;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
import com.ning.billing.util.tag.ControlTagType;
import com.ning.billing.util.tag.Tag;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
import com.google.inject.Inject;
public class DefaultInvoiceUserApi implements InvoiceUserApi {
@@ -111,6 +114,20 @@ public class DefaultInvoiceUserApi implements InvoiceUserApi {
}
@Override
+ public Pagination<Invoice> getInvoices(final Long offset, final Long limit, final TenantContext context) {
+ // Invoices will be shallow, i.e. won't contain items nor payments
+ final Pagination<InvoiceModelDao> invoiceModelDaos = dao.get(offset, limit, internalCallContextFactory.createInternalTenantContext(context));
+ return new DefaultPagination<Invoice>(invoiceModelDaos,
+ Iterators.<InvoiceModelDao, Invoice>transform(invoiceModelDaos.iterator(),
+ new Function<InvoiceModelDao, Invoice>() {
+ @Override
+ public Invoice apply(final InvoiceModelDao input) {
+ return new DefaultInvoice(input);
+ }
+ }));
+ }
+
+ @Override
public BigDecimal getAccountBalance(final UUID accountId, final TenantContext context) {
final BigDecimal result = dao.getAccountBalance(accountId, internalCallContextFactory.createInternalTenantContext(accountId, context));
return result == null ? BigDecimal.ZERO : result;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index c0ea740..c4ee207 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -32,6 +32,8 @@ import org.slf4j.LoggerFactory;
import com.ning.billing.ErrorCode;
import com.ning.billing.bus.api.PersistentBus;
import com.ning.billing.bus.api.PersistentBus.EventBusException;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.Currency;
import com.ning.billing.clock.Clock;
import com.ning.billing.invoice.api.Invoice;
@@ -41,8 +43,6 @@ import com.ning.billing.invoice.api.InvoicePaymentType;
import com.ning.billing.invoice.api.user.DefaultInvoiceAdjustmentEvent;
import com.ning.billing.invoice.notification.NextBillingDatePoster;
import com.ning.billing.util.cache.CacheControllerDispatcher;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.entity.dao.EntityDaoBase;
import com.ning.billing.util.entity.dao.EntitySqlDao;
@@ -149,21 +149,6 @@ public class DefaultInvoiceDao extends EntityDaoBase<InvoiceModelDao, Invoice, I
}
@Override
- public List<InvoiceModelDao> get(final InternalTenantContext context) {
- return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<InvoiceModelDao>>() {
- @Override
- public List<InvoiceModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
- final InvoiceSqlDao invoiceDao = entitySqlDaoWrapperFactory.become(InvoiceSqlDao.class);
-
- final List<InvoiceModelDao> invoices = invoiceDao.get(context);
- invoiceDaoHelper.populateChildren(invoices, entitySqlDaoWrapperFactory, context);
-
- return invoices;
- }
- });
- }
-
- @Override
public InvoiceModelDao getById(final UUID invoiceId, final InternalTenantContext context) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<InvoiceModelDao>() {
@Override
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index 4e6170c..8f2c668 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -26,22 +26,20 @@ import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.util.entity.dao.EntityDao;
-public interface InvoiceDao {
+public interface InvoiceDao extends EntityDao<InvoiceModelDao, Invoice, InvoiceApiException> {
void createInvoice(InvoiceModelDao invoice, List<InvoiceItemModelDao> invoiceItems,
List<InvoicePaymentModelDao> invoicePayments, boolean isRealInvoice, final Map<UUID, DateTime> callbackDateTimePerSubscriptions, InternalCallContext context);
- InvoiceModelDao getById(UUID id, InternalTenantContext context);
-
InvoiceModelDao getByNumber(Integer number, InternalTenantContext context) throws InvoiceApiException;
- List<InvoiceModelDao> get(InternalTenantContext context);
-
List<InvoiceModelDao> getInvoicesByAccount(InternalTenantContext context);
List<InvoiceModelDao> getInvoicesByAccount(LocalDate fromDate, InternalTenantContext context);
@@ -58,8 +56,6 @@ public interface InvoiceDao {
List<InvoiceModelDao> getUnpaidInvoicesByAccountId(UUID accountId, @Nullable LocalDate upToDate, InternalTenantContext context);
- void test(InternalTenantContext context);
-
// Include migrated invoices
List<InvoiceModelDao> getAllInvoicesByAccount(InternalTenantContext context);
@@ -165,9 +161,8 @@ public interface InvoiceDao {
void notifyOfPayment(InvoicePaymentModelDao invoicePayment, InternalCallContext context);
/**
- *
* @param accountId the account for which we need to rebalance the CBA
- * @param context the callcontext
+ * @param context the callcontext
*/
public void consumeExstingCBAOnAccountWithUnpaidInvoices(final UUID accountId, final InternalCallContext context);
}
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
index e34563f..9d07999 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDaoHelper.java
@@ -28,12 +28,12 @@ import javax.annotation.Nullable;
import org.joda.time.LocalDate;
import com.ning.billing.ErrorCode;
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.invoice.api.InvoiceApiException;
-import com.ning.billing.invoice.api.InvoiceItemType;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.Currency;
import com.ning.billing.entity.EntityPersistenceException;
+import com.ning.billing.invoice.api.InvoiceApiException;
+import com.ning.billing.invoice.api.InvoiceItemType;
import com.ning.billing.util.entity.dao.EntitySqlDao;
import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
@@ -223,7 +223,6 @@ public class InvoiceDaoHelper {
transInvoiceItemDao.create(item, context);
}
-
public void populateChildren(final InvoiceModelDao invoice, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalTenantContext context) {
getInvoiceItemsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
getInvoicePaymentsWithinTransaction(invoice, entitySqlDaoWrapperFactory, context);
@@ -271,6 +270,4 @@ public class InvoiceDaoHelper {
final List<InvoicePaymentModelDao> invoicePayments = invoicePaymentSqlDao.getPaymentsForInvoice(invoiceId, context);
invoice.addPayments(invoicePayments);
}
-
-
}
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
index a36cf1e..5006858 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/MockInvoiceDao.java
@@ -30,17 +30,21 @@ import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import com.ning.billing.bus.api.PersistentBus;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.invoice.api.Invoice;
import com.ning.billing.invoice.api.InvoiceApiException;
import com.ning.billing.invoice.api.user.DefaultInvoiceCreationEvent;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
+import com.ning.billing.util.entity.dao.MockEntityDaoBase;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.inject.Inject;
-public class MockInvoiceDao implements InvoiceDao {
+public class MockInvoiceDao extends MockEntityDaoBase<InvoiceModelDao, Invoice, InvoiceApiException> implements InvoiceDao {
private final PersistentBus eventBus;
private final Object monitor = new Object();
@@ -70,7 +74,7 @@ public class MockInvoiceDao implements InvoiceDao {
try {
eventBus.post(new DefaultInvoiceCreationEvent(invoice.getId(), invoice.getAccountId(),
InvoiceModelDaoHelper.getBalance(invoice), invoice.getCurrency(),
- context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()));
+ context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()));
} catch (PersistentBus.EventBusException ex) {
throw new RuntimeException(ex);
}
@@ -97,9 +101,9 @@ public class MockInvoiceDao implements InvoiceDao {
}
@Override
- public List<InvoiceModelDao> get(final InternalTenantContext context) {
+ public Pagination<InvoiceModelDao> getAll(final InternalTenantContext context) {
synchronized (monitor) {
- return new ArrayList<InvoiceModelDao>(invoices.values());
+ return new DefaultPagination<InvoiceModelDao>((long) invoices.values().size(), invoices.values().iterator());
}
}
@@ -124,7 +128,7 @@ public class MockInvoiceDao implements InvoiceDao {
synchronized (monitor) {
final UUID accountId = accountRecordIds.inverse().get(context.getAccountRecordId());
- for (final InvoiceModelDao invoice : get(context)) {
+ for (final InvoiceModelDao invoice : getAll(context)) {
if (accountId.equals(invoice.getAccountId()) && !invoice.getTargetDate().isBefore(fromDate) && !invoice.isMigrated()) {
invoicesForAccount.add(invoice);
}
@@ -195,7 +199,7 @@ public class MockInvoiceDao implements InvoiceDao {
public BigDecimal getAccountBalance(final UUID accountId, final InternalTenantContext context) {
BigDecimal balance = BigDecimal.ZERO;
- for (final InvoiceModelDao invoice : get(context)) {
+ for (final InvoiceModelDao invoice : getAll(context)) {
if (accountId.equals(invoice.getAccountId())) {
balance = balance.add(InvoiceModelDaoHelper.getBalance(invoice));
}
@@ -208,7 +212,7 @@ public class MockInvoiceDao implements InvoiceDao {
public List<InvoiceModelDao> getUnpaidInvoicesByAccountId(final UUID accountId, final LocalDate upToDate, final InternalTenantContext context) {
final List<InvoiceModelDao> unpaidInvoices = new ArrayList<InvoiceModelDao>();
- for (final InvoiceModelDao invoice : get(context)) {
+ for (final InvoiceModelDao invoice : getAll(context)) {
if (accountId.equals(invoice.getAccountId()) && (InvoiceModelDaoHelper.getBalance(invoice).compareTo(BigDecimal.ZERO) > 0) && !invoice.isMigrated()) {
unpaidInvoices.add(invoice);
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
index daed996..c5540cb 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/AccountResource.java
@@ -16,6 +16,8 @@
package com.ning.billing.jaxrs.resources;
+import java.io.IOException;
+import java.io.OutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
@@ -36,8 +38,10 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import com.ning.billing.ErrorCode;
@@ -92,12 +96,13 @@ import com.ning.billing.util.audit.AuditLogsForPayments;
import com.ning.billing.util.audit.AuditLogsForRefunds;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.Pagination;
import com.ning.billing.util.tag.ControlTagType;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -154,18 +159,37 @@ public class AccountResource extends JaxRsResourceBase {
@Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
@Produces(APPLICATION_JSON)
public Response searchAccounts(@PathParam("searchKey") final String searchKey,
+ @QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset,
+ @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit,
@QueryParam(QUERY_ACCOUNT_WITH_BALANCE) @DefaultValue("false") final Boolean accountWithBalance,
@QueryParam(QUERY_ACCOUNT_WITH_BALANCE_AND_CBA) @DefaultValue("false") final Boolean accountWithBalanceAndCBA,
@javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException {
final TenantContext tenantContext = context.createContext(request);
- final List<Account> accounts = accountUserApi.searchAccounts(searchKey, tenantContext);
- final List<AccountJson> accountsJson = ImmutableList.<AccountJson>copyOf(Collections2.transform(accounts, new Function<Account, AccountJson>() {
+ final Pagination<Account> accounts = accountUserApi.searchAccounts(searchKey, offset, limit, tenantContext);
+
+ final StreamingOutput json = new StreamingOutput() {
@Override
- public AccountJson apply(final Account account) {
- return getAccount(account, accountWithBalance, accountWithBalanceAndCBA, tenantContext);
+ public void write(final OutputStream output) throws IOException, WebApplicationException {
+ final JsonGenerator generator = mapper.getFactory().createJsonGenerator(output);
+ generator.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
+
+ generator.writeStartArray();
+ for (final Account account : accounts) {
+ final AccountJson asJson = getAccount(account, accountWithBalance, accountWithBalanceAndCBA, tenantContext);
+ generator.writeObject(asJson);
+ }
+ generator.writeEndArray();
+ generator.close();
}
- }));
- return Response.status(Status.OK).entity(accountsJson).build();
+ };
+ return Response.status(Status.OK)
+ .entity(json)
+ .header(HDR_PAGINATION_CURRENT_OFFSET, accounts.getCurrentOffset())
+ .header(HDR_PAGINATION_NEXT_OFFSET, accounts.getNextOffset())
+ .header(HDR_PAGINATION_TOTAL_NB_RESULTS, accounts.getTotalNbResults())
+ .header(HDR_PAGINATION_NB_RESULTS, accounts.getNbResults())
+ .header(HDR_PAGINATION_NB_RESULTS_FROM_OFFSET, accounts.getNbResultsFromOffset())
+ .build();
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
index 8f91ef2..07e7ac3 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxrsResource.java
@@ -40,6 +40,11 @@ public interface JaxrsResource {
public static String HDR_CREATED_BY = "X-Killbill-CreatedBy";
public static String HDR_REASON = "X-Killbill-Reason";
public static String HDR_COMMENT = "X-Killbill-Comment";
+ public static String HDR_PAGINATION_CURRENT_OFFSET = "X-Killbill-Pagination-CurrentOffset";
+ public static String HDR_PAGINATION_NEXT_OFFSET = "X-Killbill-Pagination-NextOffset";
+ public static String HDR_PAGINATION_TOTAL_NB_RESULTS = "X-Killbill-Pagination-TotalNbResults";
+ public static String HDR_PAGINATION_NB_RESULTS = "X-Killbill-Pagination-NbResults";
+ public static String HDR_PAGINATION_NB_RESULTS_FROM_OFFSET = "X-Killbill-Pagination-NbResultsFromOffset";
/*
* Patterns
@@ -62,6 +67,8 @@ public interface JaxrsResource {
public static final String QUERY_TARGET_DATE = "targetDate";
public static final String QUERY_BILLING_POLICY = "billingPolicy";
public static final String QUERY_ENTITLEMENT_POLICY = "entitlementPolicy";
+ public static final String QUERY_SEARCH_OFFSET = "offset";
+ public static final String QUERY_SEARCH_LIMIT = "limit";
public static final String QUERY_ACCOUNT_WITH_BALANCE = "accountWithBalance";
public static final String QUERY_ACCOUNT_WITH_BALANCE_AND_CBA = "accountWithBalanceAndCBA";
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
index 3915e39..c24c399 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -53,6 +53,7 @@ import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.customfield.StringCustomField;
+import com.ning.billing.util.jackson.ObjectMapper;
import com.ning.billing.util.tag.Tag;
import com.ning.billing.util.tag.TagDefinition;
@@ -64,6 +65,8 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
private static final Logger log = LoggerFactory.getLogger(JaxRsResourceBase.class);
+ protected static final ObjectMapper mapper = new ObjectMapper();
+
protected final JaxrsUriBuilder uriBuilder;
protected final TagUserApi tagUserApi;
protected final CustomFieldUserApi customFieldUserApi;
@@ -172,7 +175,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
protected LocalDate toLocalDate(final UUID accountId, final String inputDate, final TenantContext context) {
- final LocalDate maybeResult = extractLocalDate(inputDate);
+ final LocalDate maybeResult = extractLocalDate(inputDate);
if (maybeResult != null) {
return maybeResult;
}
@@ -187,10 +190,9 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
}
-
protected LocalDate toLocalDate(final Account account, final String inputDate, final TenantContext context) {
- final LocalDate maybeResult = extractLocalDate(inputDate);
+ final LocalDate maybeResult = extractLocalDate(inputDate);
if (maybeResult != null) {
return maybeResult;
}
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
index 1218a08..2af1d58 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/resources/PaymentMethodResource.java
@@ -16,8 +16,9 @@
package com.ning.billing.jaxrs.resources;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -30,8 +31,10 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.StreamingOutput;
import com.ning.billing.ObjectType;
import com.ning.billing.account.api.Account;
@@ -49,10 +52,10 @@ import com.ning.billing.util.api.CustomFieldUserApi;
import com.ning.billing.util.api.TagUserApi;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.Pagination;
-import com.google.common.base.Function;
+import com.fasterxml.jackson.core.JsonGenerator;
import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -96,34 +99,55 @@ public class PaymentMethodResource extends JaxRsResourceBase {
@Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
@Produces(APPLICATION_JSON)
public Response searchPaymentMethods(@PathParam("searchKey") final String searchKey,
+ @QueryParam(QUERY_SEARCH_OFFSET) @DefaultValue("0") final Long offset,
+ @QueryParam(QUERY_SEARCH_LIMIT) @DefaultValue("100") final Long limit,
@QueryParam(QUERY_PAYMENT_METHOD_PLUGIN_NAME) final String pluginName,
@javax.ws.rs.core.Context final HttpServletRequest request) throws PaymentApiException, AccountApiException {
final TenantContext tenantContext = context.createContext(request);
// Search the plugin(s)
- final List<PaymentMethod> paymentMethods;
+ final Pagination<PaymentMethod> paymentMethods;
if (Strings.isNullOrEmpty(pluginName)) {
- paymentMethods = paymentApi.searchPaymentMethods(searchKey, tenantContext);
+ paymentMethods = paymentApi.searchPaymentMethods(searchKey, offset, limit, tenantContext);
} else {
- paymentMethods = paymentApi.searchPaymentMethods(searchKey, pluginName, tenantContext);
+ paymentMethods = paymentApi.searchPaymentMethods(searchKey, offset, limit, pluginName, tenantContext);
}
- // Lookup the associated account(s)
final Map<UUID, Account> accounts = new HashMap<UUID, Account>();
- for (final PaymentMethod paymentMethod : paymentMethods) {
- if (accounts.get(paymentMethod.getAccountId()) == null) {
- final Account account = accountUserApi.getAccountById(paymentMethod.getAccountId(), tenantContext);
- accounts.put(paymentMethod.getAccountId(), account);
- }
- }
-
- final List<PaymentMethodJson> json = Lists.transform(paymentMethods, new Function<PaymentMethod, PaymentMethodJson>() {
+ final StreamingOutput json = new StreamingOutput() {
@Override
- public PaymentMethodJson apply(final PaymentMethod paymentMethod) {
- return PaymentMethodJson.toPaymentMethodJson(accounts.get(paymentMethod.getAccountId()), paymentMethod);
+ public void write(final OutputStream output) throws IOException, WebApplicationException {
+ final JsonGenerator generator = mapper.getFactory().createJsonGenerator(output);
+ generator.configure(JsonGenerator.Feature.AUTO_CLOSE_TARGET, false);
+
+ generator.writeStartArray();
+ for (final PaymentMethod paymentMethod : paymentMethods) {
+ // Lookup the associated account(s)
+ if (accounts.get(paymentMethod.getAccountId()) == null) {
+ final Account account;
+ try {
+ account = accountUserApi.getAccountById(paymentMethod.getAccountId(), tenantContext);
+ accounts.put(paymentMethod.getAccountId(), account);
+ } catch (AccountApiException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ final PaymentMethodJson asJson = PaymentMethodJson.toPaymentMethodJson(accounts.get(paymentMethod.getAccountId()), paymentMethod);
+ generator.writeObject(asJson);
+ }
+ generator.writeEndArray();
+ generator.close();
}
- });
- return Response.status(Status.OK).entity(json).build();
+ };
+ return Response.status(Status.OK)
+ .entity(json)
+ .header(HDR_PAGINATION_CURRENT_OFFSET, paymentMethods.getCurrentOffset())
+ .header(HDR_PAGINATION_NEXT_OFFSET, paymentMethods.getNextOffset())
+ .header(HDR_PAGINATION_TOTAL_NB_RESULTS, paymentMethods.getTotalNbResults())
+ .header(HDR_PAGINATION_NB_RESULTS, paymentMethods.getNbResults())
+ .header(HDR_PAGINATION_NB_RESULTS_FROM_OFFSET, paymentMethods.getNbResultsFromOffset())
+ .build();
}
@DELETE
diff --git a/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java b/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
index 4868c1f..f3ab64c 100644
--- a/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
+++ b/osgi-bundles/bundles/jruby/src/main/java/com/ning/billing/osgi/bundles/jruby/JRubyPaymentPlugin.java
@@ -38,6 +38,7 @@ import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.Pagination;
public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi {
@@ -168,11 +169,11 @@ public class JRubyPaymentPlugin extends JRubyPlugin implements PaymentPluginApi
}
@Override
- public List<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final TenantContext tenantContext) throws PaymentPluginApiException {
+ public Pagination<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
return callWithRuntimeAndChecking(new PluginCallback(VALIDATION_PLUGIN_TYPE.PAYMENT) {
@Override
- public List<PaymentMethodPlugin> doCall(final Ruby runtime) throws PaymentPluginApiException {
- return ((PaymentPluginApi) pluginInstance).searchPaymentMethods(searchKey, tenantContext);
+ public Pagination<PaymentMethodPlugin> doCall(final Ruby runtime) throws PaymentPluginApiException {
+ return ((PaymentPluginApi) pluginInstance).searchPaymentMethods(searchKey, offset, limit, tenantContext);
}
});
}
diff --git a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
index 600f5af..dffd273 100644
--- a/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
+++ b/osgi-bundles/tests/beatrix/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -18,6 +18,7 @@ package com.ning.billing.osgi.bundles.test;
import java.math.BigDecimal;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.UUID;
@@ -34,6 +35,7 @@ import com.ning.billing.payment.plugin.api.PaymentPluginStatus;
import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.Pagination;
public class TestPaymentPluginApi implements PaymentPluginApi {
@@ -129,8 +131,38 @@ public class TestPaymentPluginApi implements PaymentPluginApi {
}
@Override
- public List<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final TenantContext tenantContext) throws PaymentPluginApiException {
- return Collections.emptyList();
+ public Pagination<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+ return new Pagination<PaymentMethodPlugin>() {
+ @Override
+ public Long getCurrentOffset() {
+ return 0L;
+ }
+
+ @Override
+ public Long getNextOffset() {
+ return null;
+ }
+
+ @Override
+ public Long getTotalNbResults() {
+ return 0L;
+ }
+
+ @Override
+ public Long getNbResults() {
+ return null;
+ }
+
+ @Override
+ public Long getNbResultsFromOffset() {
+ return null;
+ }
+
+ @Override
+ public Iterator<PaymentMethodPlugin> iterator() {
+ return null;
+ }
+ };
}
@Override
diff --git a/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
index 804e548..0d4f742 100644
--- a/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
+++ b/osgi-bundles/tests/payment/src/test/java/com/ning/billing/osgi/bundles/test/TestPaymentPluginApi.java
@@ -18,6 +18,7 @@ package com.ning.billing.osgi.bundles.test;
import java.math.BigDecimal;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.UUID;
@@ -34,6 +35,7 @@ import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
import com.ning.billing.payment.plugin.api.RefundPluginStatus;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.Pagination;
public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
@@ -54,22 +56,27 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
public BigDecimal getAmount() {
return amount;
}
+
@Override
public DateTime getCreatedDate() {
return new DateTime();
}
+
@Override
public DateTime getEffectiveDate() {
return new DateTime();
}
+
@Override
public PaymentPluginStatus getStatus() {
return PaymentPluginStatus.PROCESSED;
}
+
@Override
public String getGatewayError() {
return null;
}
+
@Override
public String getGatewayErrorCode() {
return null;
@@ -96,22 +103,27 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
public BigDecimal getAmount() {
return someAmount;
}
+
@Override
public DateTime getCreatedDate() {
return new DateTime();
}
+
@Override
public DateTime getEffectiveDate() {
return new DateTime();
}
+
@Override
public PaymentPluginStatus getStatus() {
return PaymentPluginStatus.PROCESSED;
}
+
@Override
public String getGatewayError() {
return null;
}
+
@Override
public String getGatewayErrorCode() {
return null;
@@ -138,22 +150,27 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
public BigDecimal getAmount() {
return null;
}
+
@Override
public DateTime getCreatedDate() {
return null;
}
+
@Override
public DateTime getEffectiveDate() {
return null;
}
+
@Override
public RefundPluginStatus getStatus() {
return null;
}
+
@Override
public String getGatewayError() {
return null;
}
+
@Override
public String getGatewayErrorCode() {
return null;
@@ -194,8 +211,38 @@ public class TestPaymentPluginApi implements PaymentPluginApiWithTestControl {
}
@Override
- public List<PaymentMethodPlugin> searchPaymentMethods(final String s, final TenantContext tenantContext) throws PaymentPluginApiException {
- return Collections.emptyList();
+ public Pagination<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+ return new Pagination<PaymentMethodPlugin>() {
+ @Override
+ public Long getCurrentOffset() {
+ return 0L;
+ }
+
+ @Override
+ public Long getNextOffset() {
+ return null;
+ }
+
+ @Override
+ public Long getTotalNbResults() {
+ return 0L;
+ }
+
+ @Override
+ public Long getNbResults() {
+ return null;
+ }
+
+ @Override
+ public Long getNbResultsFromOffset() {
+ return null;
+ }
+
+ @Override
+ public Iterator<PaymentMethodPlugin> iterator() {
+ return null;
+ }
+ };
}
@Override
diff --git a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
index d0f7b65..aeb556b 100644
--- a/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
+++ b/payment/src/main/java/com/ning/billing/payment/api/DefaultPaymentApi.java
@@ -26,13 +26,14 @@ import java.util.UUID;
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
+import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.payment.core.PaymentMethodProcessor;
import com.ning.billing.payment.core.PaymentProcessor;
import com.ning.billing.payment.core.RefundProcessor;
import com.ning.billing.util.callcontext.CallContext;
-import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.util.callcontext.InternalCallContextFactory;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.Pagination;
import com.google.common.collect.ImmutableMap;
import com.google.inject.Inject;
@@ -173,13 +174,13 @@ public class DefaultPaymentApi implements PaymentApi {
}
@Override
- public List<PaymentMethod> searchPaymentMethods(final String searchKey, final TenantContext context) {
- return methodProcessor.searchPaymentMethods(searchKey, internalCallContextFactory.createInternalTenantContext(context));
+ public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final TenantContext context) {
+ return methodProcessor.searchPaymentMethods(searchKey, offset, limit, internalCallContextFactory.createInternalTenantContext(context));
}
@Override
- public List<PaymentMethod> searchPaymentMethods(final String searchKey, final String pluginName, final TenantContext context) throws PaymentApiException {
- return methodProcessor.searchPaymentMethods(searchKey, pluginName, internalCallContextFactory.createInternalTenantContext(context));
+ public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final String pluginName, final TenantContext context) throws PaymentApiException {
+ return methodProcessor.searchPaymentMethods(searchKey, offset, limit, pluginName, internalCallContextFactory.createInternalTenantContext(context));
}
@Override
diff --git a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
index cd50517..9f05903 100644
--- a/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/com/ning/billing/payment/core/PaymentMethodProcessor.java
@@ -32,8 +32,12 @@ import org.slf4j.LoggerFactory;
import com.ning.billing.ErrorCode;
import com.ning.billing.account.api.Account;
import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountInternalApi;
import com.ning.billing.bus.api.PersistentBus;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.commons.locker.GlobalLocker;
+import com.ning.billing.invoice.api.InvoiceInternalApi;
import com.ning.billing.osgi.api.OSGIServiceRegistration;
import com.ning.billing.payment.api.DefaultPaymentMethod;
import com.ning.billing.payment.api.PaymentApiException;
@@ -48,16 +52,16 @@ import com.ning.billing.payment.plugin.api.PaymentPluginApiException;
import com.ning.billing.payment.provider.DefaultNoOpPaymentMethodPlugin;
import com.ning.billing.payment.provider.DefaultPaymentMethodInfoPlugin;
import com.ning.billing.payment.provider.ExternalPaymentProviderPlugin;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.util.dao.NonEntityDao;
-import com.ning.billing.account.api.AccountInternalApi;
-import com.ning.billing.invoice.api.InvoiceInternalApi;
import com.ning.billing.tag.TagInternalApi;
+import com.ning.billing.util.dao.NonEntityDao;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
import com.google.common.base.Function;
+import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
import com.google.inject.Inject;
import com.google.inject.name.Named;
@@ -153,49 +157,59 @@ public class PaymentMethodProcessor extends ProcessorBase {
return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
}
- public List<PaymentMethod> searchPaymentMethods(final String searchKey, final InternalTenantContext internalTenantContext) {
- final List<PaymentMethod> results = new LinkedList<PaymentMethod>();
+ public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final InternalTenantContext internalTenantContext) {
+ // Note that we cannot easily do streaming here, since we would have to rely on the statistics
+ // returned by the Pagination objects from the plugins and we probably don't want to do that (if
+ // one plugin gets it wrong, it may starve the others).
+ final List<PaymentMethod> allResults = new LinkedList<PaymentMethod>();
- // Search in all plugins
+ // Search in all plugins (we treat the full set of results as a union with respect to offset/limit)
for (final String pluginName : getAvailablePlugins()) {
try {
- results.addAll(searchPaymentMethods(searchKey, pluginName, internalTenantContext));
+ final Pagination<PaymentMethod> paymentMethods = searchPaymentMethods(searchKey, 0L, Long.MAX_VALUE, pluginName, internalTenantContext);
+ allResults.addAll(ImmutableList.<PaymentMethod>copyOf(paymentMethods));
+ if (allResults.size() > offset + limit) {
+ break;
+ }
} catch (PaymentApiException e) {
log.warn("Error while searching plugin " + pluginName, e);
// Non-fatal, continue to search other plugins
}
}
- return results;
+ return DefaultPagination.<PaymentMethod>build(offset, limit, allResults);
}
- public List<PaymentMethod> searchPaymentMethods(final String searchKey, final String pluginName, final InternalTenantContext internalTenantContext) throws PaymentApiException {
+ public Pagination<PaymentMethod> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final String pluginName, final InternalTenantContext internalTenantContext) throws PaymentApiException {
final PaymentPluginApi pluginApi = getPaymentPluginApi(pluginName);
- final List<PaymentMethodPlugin> paymentMethods;
+ final Pagination<PaymentMethodPlugin> paymentMethods;
try {
- paymentMethods = pluginApi.searchPaymentMethods(searchKey, buildTenantContext(internalTenantContext));
+ paymentMethods = pluginApi.searchPaymentMethods(searchKey, offset, limit, buildTenantContext(internalTenantContext));
} catch (PaymentPluginApiException e) {
throw new PaymentApiException(e, ErrorCode.PAYMENT_PLUGIN_SEARCH_PAYMENT_METHODS, pluginName, searchKey);
}
- final List<PaymentMethod> results = new LinkedList<PaymentMethod>();
- for (final PaymentMethodPlugin paymentMethodPlugin : paymentMethods) {
- if (paymentMethodPlugin.getKbPaymentMethodId() == null) {
- // Garbage from the plugin?
- log.debug("Plugin {} returned a payment method without a kbPaymentMethodId for searchKey {}", pluginName, searchKey);
- continue;
- }
-
- final PaymentMethodModelDao paymentMethodModelDao = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodPlugin.getKbPaymentMethodId(), internalTenantContext);
- if (paymentMethodModelDao == null) {
- log.warn("Unable to find payment method id " + paymentMethodPlugin.getKbPaymentMethodId() + " present in plugin " + pluginName);
- continue;
- }
-
- results.add(new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin));
- }
-
- return results;
+ return new DefaultPagination<PaymentMethod>(paymentMethods,
+ Iterators.<PaymentMethod>filter(Iterators.<PaymentMethodPlugin, PaymentMethod>transform(paymentMethods.iterator(),
+ new Function<PaymentMethodPlugin, PaymentMethod>() {
+ @Override
+ public PaymentMethod apply(final PaymentMethodPlugin paymentMethodPlugin) {
+ if (paymentMethodPlugin.getKbPaymentMethodId() == null) {
+ // Garbage from the plugin?
+ log.debug("Plugin {} returned a payment method without a kbPaymentMethodId for searchKey {}", pluginName, searchKey);
+ return null;
+ }
+
+ final PaymentMethodModelDao paymentMethodModelDao = paymentDao.getPaymentMethodIncludedDeleted(paymentMethodPlugin.getKbPaymentMethodId(), internalTenantContext);
+ if (paymentMethodModelDao == null) {
+ log.warn("Unable to find payment method id " + paymentMethodPlugin.getKbPaymentMethodId() + " present in plugin " + pluginName);
+ return null;
+ }
+
+ return new DefaultPaymentMethod(paymentMethodModelDao, paymentMethodPlugin);
+ }
+ }),
+ Predicates.<PaymentMethod>notNull()));
}
public PaymentMethod getExternalPaymentMethod(final Account account, final InternalTenantContext context) throws PaymentApiException {
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
index 449324a..f934d59 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/DefaultNoOpPaymentProviderPlugin.java
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.clock.Clock;
import com.ning.billing.payment.api.PaymentMethodPlugin;
import com.ning.billing.payment.plugin.api.NoOpPaymentPluginApi;
import com.ning.billing.payment.plugin.api.PaymentInfoPlugin;
@@ -35,7 +36,8 @@ import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
import com.ning.billing.payment.plugin.api.RefundPluginStatus;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
-import com.ning.billing.clock.Clock;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -157,8 +159,8 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
}
@Override
- public List<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final TenantContext tenantContext) throws PaymentPluginApiException {
- return ImmutableList.<PaymentMethodPlugin>copyOf(Iterables.<PaymentMethodPlugin>filter(Iterables.<PaymentMethodPlugin>concat(paymentMethods.values()), new Predicate<PaymentMethodPlugin>() {
+ public Pagination<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+ final ImmutableList<PaymentMethodPlugin> results = ImmutableList.<PaymentMethodPlugin>copyOf(Iterables.<PaymentMethodPlugin>filter(Iterables.<PaymentMethodPlugin>concat(paymentMethods.values()), new Predicate<PaymentMethodPlugin>() {
@Override
public boolean apply(final PaymentMethodPlugin input) {
return (input.getAddress1() != null && input.getAddress1().contains(searchKey)) ||
@@ -170,6 +172,7 @@ public class DefaultNoOpPaymentProviderPlugin implements NoOpPaymentPluginApi {
(input.getCountry() != null && input.getCountry().contains(searchKey));
}
}));
+ return DefaultPagination.<PaymentMethodPlugin>build(offset, limit, results);
}
@Override
diff --git a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
index b1b6915..396a17d 100644
--- a/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
+++ b/payment/src/main/java/com/ning/billing/payment/provider/ExternalPaymentProviderPlugin.java
@@ -34,8 +34,11 @@ import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
import com.ning.billing.payment.plugin.api.RefundPluginStatus;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterators;
import com.google.inject.Inject;
/**
@@ -98,8 +101,8 @@ public class ExternalPaymentProviderPlugin implements PaymentPluginApi {
}
@Override
- public List<PaymentMethodPlugin> searchPaymentMethods(final String s, final TenantContext tenantContext) throws PaymentPluginApiException {
- return ImmutableList.<PaymentMethodPlugin>of();
+ public Pagination<PaymentMethodPlugin> searchPaymentMethods(final String searchKy, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+ return new DefaultPagination<PaymentMethodPlugin>(0L, Iterators.<PaymentMethodPlugin>emptyIterator());
}
@Override
diff --git a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
index 3768b18..0d383c6 100644
--- a/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
+++ b/payment/src/test/java/com/ning/billing/payment/provider/MockPaymentProviderPlugin.java
@@ -37,6 +37,8 @@ import com.ning.billing.payment.plugin.api.RefundInfoPlugin;
import com.ning.billing.payment.plugin.api.RefundPluginStatus;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
@@ -146,8 +148,8 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
}
@Override
- public List<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final TenantContext tenantContext) throws PaymentPluginApiException {
- return ImmutableList.<PaymentMethodPlugin>copyOf(Iterables.<PaymentMethodPlugin>filter(paymentMethods.values(), new Predicate<PaymentMethodPlugin>() {
+ public Pagination<PaymentMethodPlugin> searchPaymentMethods(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) throws PaymentPluginApiException {
+ final ImmutableList<PaymentMethodPlugin> results = ImmutableList.<PaymentMethodPlugin>copyOf(Iterables.<PaymentMethodPlugin>filter(paymentMethods.values(), new Predicate<PaymentMethodPlugin>() {
@Override
public boolean apply(final PaymentMethodPlugin input) {
return (input.getAddress1() != null && input.getAddress1().contains(searchKey)) ||
@@ -159,6 +161,7 @@ public class MockPaymentProviderPlugin implements NoOpPaymentPluginApi {
(input.getCountry() != null && input.getCountry().contains(searchKey));
}
}));
+ return DefaultPagination.<PaymentMethodPlugin>build(offset, limit, results);
}
@Override
pom.xml 2(+1 -1)
diff --git a/pom.xml b/pom.xml
index 274a9b5..68be740 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
<parent>
<artifactId>killbill-oss-parent</artifactId>
<groupId>com.ning.billing</groupId>
- <version>0.4.18</version>
+ <version>0.5.0-SNAPSHOT</version>
</parent>
<artifactId>killbill</artifactId>
<version>0.6.18-SNAPSHOT</version>
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
index 9e09c6c..477a305 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDao.java
@@ -16,13 +16,13 @@
package com.ning.billing.util.entity.dao;
-import java.util.List;
import java.util.UUID;
import com.ning.billing.BillingExceptionBase;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.util.entity.Entity;
+import com.ning.billing.util.entity.Pagination;
public interface EntityDao<M extends EntityModelDao<E>, E extends Entity, U extends BillingExceptionBase> {
@@ -34,7 +34,11 @@ public interface EntityDao<M extends EntityModelDao<E>, E extends Entity, U exte
public M getById(UUID id, InternalTenantContext context);
- public List<M> get(InternalTenantContext context);
+ public Pagination<M> getAll(InternalTenantContext context);
+
+ public Pagination<M> get(Long offset, Long rowCount, InternalTenantContext context);
+
+ public Long getCount(InternalTenantContext context);
public void test(InternalTenantContext context);
}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java
index 1c2704c..f2e2462 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntityDaoBase.java
@@ -16,14 +16,16 @@
package com.ning.billing.util.entity.dao;
-import java.util.List;
+import java.util.Iterator;
import java.util.UUID;
import com.ning.billing.BillingExceptionBase;
-import com.ning.billing.util.audit.ChangeType;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.entity.DefaultPagination;
import com.ning.billing.util.entity.Entity;
+import com.ning.billing.util.entity.Pagination;
public abstract class EntityDaoBase<M extends EntityModelDao<E>, E extends Entity, U extends BillingExceptionBase> implements EntityDao<M, E, U> {
@@ -71,6 +73,10 @@ public abstract class EntityDaoBase<M extends EntityModelDao<E>, E extends Entit
protected abstract U generateAlreadyExistsException(final M entity, final InternalCallContext context);
+ protected String getNaturalOrderingColumns() {
+ return "recordId";
+ }
+
@Override
public Long getRecordId(final UUID id, final InternalTenantContext context) {
return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Long>() {
@@ -108,13 +114,38 @@ public abstract class EntityDaoBase<M extends EntityModelDao<E>, E extends Entit
}
@Override
- public List<M> get(final InternalTenantContext context) {
- return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<M>>() {
+ public Pagination<M> getAll(final InternalTenantContext context) {
+ // We usually always want to wrap our queries in an EntitySqlDaoTransactionWrapper... except here.
+ // Since we want to stream the results out, we don't want to auto-commit when this method returns.
+ final EntitySqlDao<M, E> sqlDao = transactionalSqlDao.onDemand(realSqlDao);
+
+ // Note: we need to perform the count before streaming the results, as the connection
+ // will be busy as we stream the results out. This is also why we cannot use
+ // SQL_CALC_FOUND_ROWS / FOUND_ROWS (which may ne be faster anyways).
+ final Long count = sqlDao.getCount(context);
+
+ final Iterator<M> results = sqlDao.getAll(context);
+ return new DefaultPagination<M>(count, results);
+ }
+
+ @Override
+ public Pagination<M> get(final Long offset, final Long rowCount, final InternalTenantContext context) {
+ // See notes above
+ final EntitySqlDao<M, E> sqlDao = transactionalSqlDao.onDemand(realSqlDao);
+ final Long count = sqlDao.getCount(context);
+
+ final Iterator<M> results = sqlDao.get(offset, rowCount, getNaturalOrderingColumns(), context);
+ return new DefaultPagination<M>(offset, rowCount, count, results);
+ }
+
+ @Override
+ public Long getCount(final InternalTenantContext context) {
+ return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Long>() {
@Override
- public List<M> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+ public Long inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
final EntitySqlDao<M, E> transactional = entitySqlDaoWrapperFactory.become(realSqlDao);
- return transactional.get(context);
+ return transactional.getCount(context);
}
});
}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
index 662cca8..d6f8fbe 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDao.java
@@ -16,26 +16,28 @@
package com.ning.billing.util.entity.dao;
+import java.util.Iterator;
import java.util.List;
import org.skife.jdbi.v2.sqlobject.Bind;
import org.skife.jdbi.v2.sqlobject.BindBean;
import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.FetchSize;
import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.entity.EntityPersistenceException;
import com.ning.billing.util.audit.ChangeType;
import com.ning.billing.util.cache.Cachable;
import com.ning.billing.util.cache.Cachable.CacheType;
import com.ning.billing.util.cache.CachableKey;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.util.dao.AuditSqlDao;
import com.ning.billing.util.dao.HistorySqlDao;
import com.ning.billing.util.entity.Entity;
-import com.ning.billing.entity.EntityPersistenceException;
// TODO get rid of Transmogrifier, but code does not compile even if we create the
// method public <T> T become(Class<T> typeToBecome); ?
@@ -66,10 +68,23 @@ public interface EntitySqlDao<M extends EntityModelDao<E>, E extends Entity> ext
@BindBean final InternalTenantContext context);
@SqlQuery
- public List<M> get(@BindBean final InternalTenantContext context);
+ // Magic value to force MySQL to stream from the database
+ // See http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html (ResultSet)
+ @FetchSize(Integer.MIN_VALUE)
+ public Iterator<M> getAll(@BindBean final InternalTenantContext context);
- @SqlUpdate
- public void test(@BindBean final InternalTenantContext context);
+ @SqlQuery
+ // Magic value to force MySQL to stream from the database
+ // See http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.html (ResultSet)
+ @FetchSize(Integer.MIN_VALUE)
+ public Iterator<M> get(@Bind("offset") final Long offset,
+ @Bind("rowCount") final Long rowCount,
+ @Bind("orderBy") final String orderBy,
+ @BindBean final InternalTenantContext context);
+ @SqlQuery
+ public Long getCount(@BindBean final InternalTenantContext context);
+ @SqlUpdate
+ public void test(@BindBean final InternalTenantContext context);
}
diff --git a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
index db23b1f..db6160f 100644
--- a/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
+++ b/util/src/main/java/com/ning/billing/util/entity/dao/EntitySqlDaoTransactionalJdbiWrapper.java
@@ -21,8 +21,8 @@ import org.skife.jdbi.v2.Transaction;
import org.skife.jdbi.v2.TransactionIsolationLevel;
import org.skife.jdbi.v2.TransactionStatus;
-import com.ning.billing.util.cache.CacheControllerDispatcher;
import com.ning.billing.clock.Clock;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.entity.Entity;
@@ -71,6 +71,10 @@ public class EntitySqlDaoTransactionalJdbiWrapper {
return entitySqlDao.inTransaction(TransactionIsolationLevel.READ_COMMITTED, new JdbiTransaction<ReturnType, EntityModelDao<Entity>, Entity>(entitySqlDaoTransactionWrapper));
}
+ public <M extends EntityModelDao<E>, E extends Entity, T extends EntitySqlDao<M, E>> T onDemand(final Class<T> sqlObjectType) {
+ return dbi.onDemand(sqlObjectType);
+ }
+
/**
* @param entitySqlDaoTransactionWrapper transaction to execute
* @param <ReturnType> object type to return from the transaction
diff --git a/util/src/main/java/com/ning/billing/util/entity/DefaultPagination.java b/util/src/main/java/com/ning/billing/util/entity/DefaultPagination.java
new file mode 100644
index 0000000..0000879
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/entity/DefaultPagination.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+
+public class DefaultPagination<T> implements Pagination<T> {
+
+ private final Long currentOffset;
+ private final Long approximateNbResults;
+ private final Long approximateTotalNbResults;
+ private final Iterator<T> delegateIterator;
+
+ // Builder when the streaming API can't be used
+ public static <T> Pagination<T> build(final Long offset, final Long limit, final Collection<T> elements) {
+ final List<T> allResults = ImmutableList.<T>copyOf(elements);
+
+ final List<T> results;
+ if (offset >= allResults.size()) {
+ results = ImmutableList.<T>of();
+ } else if (offset + limit > allResults.size()) {
+ results = allResults.subList(offset.intValue(), allResults.size());
+ } else {
+ results = allResults.subList(offset.intValue(), offset.intValue() + limit.intValue());
+ }
+ return new DefaultPagination<T>(offset, (long) results.size(), (long) allResults.size(), results.iterator());
+ }
+
+ // Constructor for DAO -> API bridge
+ public DefaultPagination(final Pagination original, final Iterator<T> delegate) {
+ this(original.getCurrentOffset(), original.getNbResults(), original.getTotalNbResults(), delegate);
+ }
+
+ // Constructor for DAO getAll calls
+ public DefaultPagination(final Long approximateTotalNbResults, final Iterator<T> results) {
+ this(0L, approximateTotalNbResults, approximateTotalNbResults, results);
+ }
+
+ public DefaultPagination(final Long currentOffset, final Long approximateNbResults,
+ final Long approximateTotalNbResults, final Iterator<T> delegateIterator) {
+ this.currentOffset = currentOffset;
+ this.approximateNbResults = approximateNbResults;
+ this.approximateTotalNbResults = approximateTotalNbResults;
+ this.delegateIterator = delegateIterator;
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return delegateIterator;
+ }
+
+ @Override
+ public Long getCurrentOffset() {
+ return currentOffset;
+ }
+
+ @Override
+ public Long getNextOffset() {
+ return currentOffset + approximateNbResults;
+ }
+
+ @Override
+ public Long getTotalNbResults() {
+ return approximateTotalNbResults;
+ }
+
+ @Override
+ public Long getNbResults() {
+ return approximateNbResults;
+ }
+
+ @Override
+ public Long getNbResultsFromOffset() {
+ return approximateNbResults - getNextOffset() + 1;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("DefaultPagination{");
+ sb.append("currentOffset=").append(currentOffset);
+ sb.append(", nextOffset=").append(getNextOffset());
+ sb.append(", approximateNbResults=").append(approximateNbResults);
+ sb.append(", approximateTotalNbResults=").append(approximateTotalNbResults);
+ sb.append(", approximateNbResultsFromOffset=").append(getNbResultsFromOffset());
+ sb.append('}');
+ return sb.toString();
+ }
+
+ // Expensive! Will compare the content of the iterator
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final DefaultPagination that = (DefaultPagination) o;
+
+ if (approximateNbResults != null ? !approximateNbResults.equals(that.approximateNbResults) : that.approximateNbResults != null) {
+ return false;
+ }
+ if (approximateTotalNbResults != null ? !approximateTotalNbResults.equals(that.approximateTotalNbResults) : that.approximateTotalNbResults != null) {
+ return false;
+ }
+ if (currentOffset != null ? !currentOffset.equals(that.currentOffset) : that.currentOffset != null) {
+ return false;
+ }
+ if (delegateIterator != null ? !ImmutableList.<T>copyOf(delegateIterator).equals(ImmutableList.<T>copyOf(that.delegateIterator)) : that.delegateIterator != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = currentOffset != null ? currentOffset.hashCode() : 0;
+ result = 31 * result + (approximateNbResults != null ? approximateNbResults.hashCode() : 0);
+ result = 31 * result + (approximateTotalNbResults != null ? approximateTotalNbResults.hashCode() : 0);
+ result = 31 * result + (delegateIterator != null ? delegateIterator.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
index 8bf21dc..019c222 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/DefaultTagDefinitionDao.java
@@ -17,6 +17,7 @@
package com.ning.billing.util.tag.dao;
import java.util.Collection;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -29,25 +30,26 @@ import org.slf4j.LoggerFactory;
import com.ning.billing.BillingExceptionBase;
import com.ning.billing.ErrorCode;
import com.ning.billing.bus.api.PersistentBus;
-import com.ning.billing.util.api.TagDefinitionApiException;
-import com.ning.billing.util.audit.ChangeType;
-import com.ning.billing.util.cache.CacheControllerDispatcher;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
import com.ning.billing.clock.Clock;
+import com.ning.billing.events.TagDefinitionInternalEvent;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.audit.ChangeType;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
import com.ning.billing.util.dao.NonEntityDao;
import com.ning.billing.util.entity.dao.EntityDaoBase;
import com.ning.billing.util.entity.dao.EntitySqlDao;
import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
-import com.ning.billing.events.TagDefinitionInternalEvent;
import com.ning.billing.util.tag.ControlTagType;
import com.ning.billing.util.tag.TagDefinition;
import com.ning.billing.util.tag.api.user.TagEventBuilder;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
+import com.google.common.collect.Iterators;
import com.google.inject.Inject;
public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao, TagDefinition, TagDefinitionApiException> implements TagDefinitionDao {
@@ -71,8 +73,10 @@ public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao
@Override
public List<TagDefinitionModelDao> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
// Get user definitions from the database
+ final TagDefinitionSqlDao tagDefinitionSqlDao = entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class);
+ final Iterator<TagDefinitionModelDao> all = tagDefinitionSqlDao.getAll(context);
final List<TagDefinitionModelDao> definitionList = new LinkedList<TagDefinitionModelDao>();
- definitionList.addAll(entitySqlDaoWrapperFactory.become(TagDefinitionSqlDao.class).get(context));
+ Iterators.addAll(definitionList, all);
// Add control tag definitions
for (final ControlTagType controlTag : ControlTagType.values()) {
@@ -240,7 +244,7 @@ public class DefaultTagDefinitionDao extends EntityDaoBase<TagDefinitionModelDao
tagEventBuilder.newControlTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition,
context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken()) :
tagEventBuilder.newUserTagDefinitionCreationEvent(tagDefinition.getId(), tagDefinition,
- context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+ context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
break;
case DELETE:
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionDao.java
index 76dcceb..6e9e6c4 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDefinitionDao.java
@@ -20,16 +20,16 @@ import java.util.Collection;
import java.util.List;
import java.util.UUID;
-import com.ning.billing.util.api.TagDefinitionApiException;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.entity.dao.EntityDao;
+import com.ning.billing.util.tag.TagDefinition;
-public interface TagDefinitionDao {
+public interface TagDefinitionDao extends EntityDao<TagDefinitionModelDao, TagDefinition, TagDefinitionApiException> {
public List<TagDefinitionModelDao> getTagDefinitions(InternalTenantContext context);
- public TagDefinitionModelDao getById(UUID definitionId, InternalTenantContext context);
-
public TagDefinitionModelDao getByName(String definitionName, InternalTenantContext context);
public List<TagDefinitionModelDao> getByIds(Collection<UUID> definitionIds, InternalTenantContext context);
diff --git a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
index 5ee87d6..646d457 100644
--- a/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/entity/dao/EntitySqlDao.sql.stg
@@ -133,13 +133,32 @@ allHistoryTableValues() ::= <<
CHECK_TENANT(prefix) ::= "<prefix>tenant_record_id = :tenantRecordId"
AND_CHECK_TENANT(prefix) ::= "and <CHECK_TENANT(prefix)>"
-get(limit) ::= <<
+getAll() ::= <<
select
<allTableFields("t.")>
from <tableName()> t
where <CHECK_TENANT("t.")>
<andCheckSoftDeletionWithComma("t.")>
-<if(limit)>limit :limit<endif>
+;
+>>
+
+get(offset, rowCount, orderBy) ::= <<
+select
+<allTableFields("t.")>
+from <tableName()> t
+where <CHECK_TENANT("t.")>
+<andCheckSoftDeletionWithComma("t.")>
+order by :orderBy
+limit :offset, :rowCount
+;
+>>
+
+getCount() ::= <<
+select
+count(1) as count
+from <tableName()> t
+where <CHECK_TENANT("t.")>
+<andCheckSoftDeletionWithComma("t.")>
;
>>
diff --git a/util/src/test/java/com/ning/billing/mock/api/MockAccountUserApi.java b/util/src/test/java/com/ning/billing/mock/api/MockAccountUserApi.java
index 488b8d8..c29d635 100644
--- a/util/src/test/java/com/ning/billing/mock/api/MockAccountUserApi.java
+++ b/util/src/test/java/com/ning/billing/mock/api/MockAccountUserApi.java
@@ -16,7 +16,6 @@
package com.ning.billing.mock.api;
-import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -33,6 +32,8 @@ import com.ning.billing.catalog.api.Currency;
import com.ning.billing.mock.MockAccountBuilder;
import com.ning.billing.util.callcontext.CallContext;
import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.entity.DefaultPagination;
+import com.ning.billing.util.entity.Pagination;
public class MockAccountUserApi implements AccountUserApi {
@@ -107,7 +108,7 @@ public class MockAccountUserApi implements AccountUserApi {
}
@Override
- public List<Account> searchAccounts(final String searchKey, final TenantContext tenantContext) {
+ public Pagination<Account> searchAccounts(final String searchKey, final Long offset, final Long limit, final TenantContext tenantContext) {
final List<Account> results = new LinkedList<Account>();
for (final Account account : accounts) {
if ((account.getName() != null && account.getName().contains(searchKey)) ||
@@ -117,12 +118,12 @@ public class MockAccountUserApi implements AccountUserApi {
results.add(account);
}
}
- return results;
+ return DefaultPagination.<Account>build(offset, limit, results);
}
@Override
- public List<Account> getAccounts(final TenantContext context) {
- return new ArrayList<Account>(accounts);
+ public Pagination<Account> getAccounts(final Long offset, final Long limit, final TenantContext context) {
+ return DefaultPagination.<Account>build(offset, limit, accounts);
}
@Override
diff --git a/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java b/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
index 0f7f467..8521cb9 100644
--- a/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
+++ b/util/src/test/java/com/ning/billing/util/customfield/dao/MockCustomFieldDao.java
@@ -21,8 +21,8 @@ import java.util.List;
import java.util.UUID;
import com.ning.billing.ObjectType;
-import com.ning.billing.util.api.CustomFieldApiException;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.api.CustomFieldApiException;
import com.ning.billing.util.customfield.CustomField;
import com.ning.billing.util.entity.dao.MockEntityDaoBase;
@@ -31,7 +31,7 @@ public class MockCustomFieldDao extends MockEntityDaoBase<CustomFieldModelDao, C
@Override
public List<CustomFieldModelDao> getCustomFieldsForObject(final UUID objectId, final ObjectType objectType, final InternalTenantContext context) {
final List<CustomFieldModelDao> result = new ArrayList<CustomFieldModelDao>();
- final List<CustomFieldModelDao> all = get(context);
+ final Iterable<CustomFieldModelDao> all = getAll(context);
for (final CustomFieldModelDao cur : all) {
if (cur.getObjectId().equals(objectId) && cur.getObjectType() == objectType) {
result.add(cur);
diff --git a/util/src/test/java/com/ning/billing/util/dao/TestPagination.java b/util/src/test/java/com/ning/billing/util/dao/TestPagination.java
new file mode 100644
index 0000000..0f0832a
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/dao/TestPagination.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.dao;
+
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
+import com.ning.billing.util.tag.dao.TagDefinitionModelDao;
+import com.ning.billing.util.tag.dao.TagDefinitionSqlDao;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestPagination extends UtilTestSuiteWithEmbeddedDB {
+
+ @Test(groups = "slow", description = "Test Pagination: basic SqlDAO and DAO calls")
+ public void testTagDefinitionsPagination() throws Exception {
+ final TagDefinitionSqlDao tagDefinitionSqlDao = dbi.onDemand(TagDefinitionSqlDao.class);
+
+ for (int i = 0; i < 10; i++) {
+ final String definitionName = "name-" + i;
+ final String description = "description-" + i;
+ tagDefinitionDao.create(definitionName, description, internalCallContext);
+ }
+
+ // Tests via SQL dao directly
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionSqlDao.getAll(internalCallContext)).size(), 10);
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionSqlDao.get(0L, 100L, "recordId", internalCallContext)).size(), 10);
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionSqlDao.get(5L, 100L, "recordId", internalCallContext)).size(), 5);
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionSqlDao.get(5L, 10L, "recordId", internalCallContext)).size(), 5);
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionSqlDao.get(0L, 5L, "recordId", internalCallContext)).size(), 5);
+ for (int i = 0; i < 10; i++) {
+ final List<TagDefinitionModelDao> tagDefinitions = ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionSqlDao.get(0L, (long) i, "recordId", internalCallContext));
+ Assert.assertEquals(tagDefinitions.size(), i);
+
+ for (int j = 0; j < tagDefinitions.size(); j++) {
+ Assert.assertEquals(tagDefinitions.get(j).getName(), "name-" + j);
+ Assert.assertEquals(tagDefinitions.get(j).getDescription(), "description-" + j);
+ }
+ }
+
+ // Tests via DAO (to test EntityDaoBase)
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionDao.getAll(internalCallContext)).size(), 10);
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionDao.get(0L, 100L, internalCallContext)).size(), 10);
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionDao.get(5L, 100L, internalCallContext)).size(), 5);
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionDao.get(5L, 10L, internalCallContext)).size(), 5);
+ Assert.assertEquals(ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionDao.get(0L, 5L, internalCallContext)).size(), 5);
+ for (int i = 0; i < 10; i++) {
+ final List<TagDefinitionModelDao> tagDefinitions = ImmutableList.<TagDefinitionModelDao>copyOf(tagDefinitionDao.get(0L, (long) i, internalCallContext));
+ Assert.assertEquals(tagDefinitions.size(), i);
+
+ for (int j = 0; j < tagDefinitions.size(); j++) {
+ Assert.assertEquals(tagDefinitions.get(j).getName(), "name-" + j);
+ Assert.assertEquals(tagDefinitions.get(j).getDescription(), "description-" + j);
+ }
+ }
+ }
+}
diff --git a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
index 4daf73a..bc1c714 100644
--- a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
+++ b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritance.java
@@ -102,29 +102,30 @@ public class TestStringTemplateInheritance extends UtilTestSuiteNoDB {
"where t.target_record_id = :targetRecordId\n" +
"and t.tenant_record_id = :tenantRecordId\n" +
";");
- Assert.assertEquals(kombucha.getInstanceOf("get").toString(), "select\n" +
- " t.record_id\n" +
- ", t.id\n" +
- ", t.tea\n" +
- ", t.mushroom\n" +
- ", t.sugar\n" +
- ", t.account_record_id\n" +
- ", t.tenant_record_id\n" +
- "from kombucha t\n" +
- "where t.tenant_record_id = :tenantRecordId\n" +
- ";");
- Assert.assertEquals(kombucha.getInstanceOf("get", ImmutableMap.<String, String>of("limit", "12")).toString(), "select\n" +
- " t.record_id\n" +
- ", t.id\n" +
- ", t.tea\n" +
- ", t.mushroom\n" +
- ", t.sugar\n" +
- ", t.account_record_id\n" +
- ", t.tenant_record_id\n" +
- "from kombucha t\n" +
- "where t.tenant_record_id = :tenantRecordId\n" +
- "limit :limit\n" +
- ";");
+ Assert.assertEquals(kombucha.getInstanceOf("getAll").toString(), "select\n" +
+ " t.record_id\n" +
+ ", t.id\n" +
+ ", t.tea\n" +
+ ", t.mushroom\n" +
+ ", t.sugar\n" +
+ ", t.account_record_id\n" +
+ ", t.tenant_record_id\n" +
+ "from kombucha t\n" +
+ "where t.tenant_record_id = :tenantRecordId\n" +
+ ";");
+ Assert.assertEquals(kombucha.getInstanceOf("get", ImmutableMap.<String, String>of("orderBy", "recordId", "offset", "3", "rowCount", "12")).toString(), "select\n" +
+ " t.record_id\n" +
+ ", t.id\n" +
+ ", t.tea\n" +
+ ", t.mushroom\n" +
+ ", t.sugar\n" +
+ ", t.account_record_id\n" +
+ ", t.tenant_record_id\n" +
+ "from kombucha t\n" +
+ "where t.tenant_record_id = :tenantRecordId\n" +
+ "order by :orderBy\n" +
+ "limit :offset, :rowCount\n" +
+ ";");
Assert.assertEquals(kombucha.getInstanceOf("test").toString(), "select\n" +
" t.record_id\n" +
", t.id\n" +
diff --git a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritanceWithJdbi.java b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritanceWithJdbi.java
index 24abcd5..0b2cad2 100644
--- a/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritanceWithJdbi.java
+++ b/util/src/test/java/com/ning/billing/util/dao/TestStringTemplateInheritanceWithJdbi.java
@@ -20,7 +20,6 @@ import org.skife.jdbi.v2.sqlobject.SqlQuery;
import org.testng.Assert;
import org.testng.annotations.Test;
-import com.ning.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
import com.ning.billing.util.UtilTestSuiteWithEmbeddedDB;
import com.ning.billing.util.entity.Entity;
import com.ning.billing.util.entity.dao.EntityModelDao;
@@ -48,6 +47,6 @@ public class TestStringTemplateInheritanceWithJdbi extends UtilTestSuiteWithEmbe
Assert.assertEquals(dao.isIsTimeForKombucha(), clock.getUTCNow().getHourOfDay() == 17);
// Verify inherited templates
- Assert.assertEquals(dao.get(internalCallContext).size(), 0);
+ Assert.assertFalse(dao.getAll(internalCallContext).hasNext());
}
}
diff --git a/util/src/test/java/com/ning/billing/util/entity/dao/MockEntityDaoBase.java b/util/src/test/java/com/ning/billing/util/entity/dao/MockEntityDaoBase.java
index b7ba4a2..d51217e 100644
--- a/util/src/test/java/com/ning/billing/util/entity/dao/MockEntityDaoBase.java
+++ b/util/src/test/java/com/ning/billing/util/entity/dao/MockEntityDaoBase.java
@@ -26,8 +26,11 @@ import java.util.concurrent.atomic.AtomicLong;
import com.ning.billing.BillingExceptionBase;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.entity.DefaultPagination;
import com.ning.billing.util.entity.Entity;
+import com.ning.billing.util.entity.Pagination;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
public class MockEntityDaoBase<M extends EntityModelDao<E>, E extends Entity, U extends BillingExceptionBase> implements EntityDao<M, E, U> {
@@ -62,12 +65,22 @@ public class MockEntityDaoBase<M extends EntityModelDao<E>, E extends Entity, U
}
@Override
- public List<M> get(final InternalTenantContext context) {
+ public Pagination<M> getAll(final InternalTenantContext context) {
final List<M> result = new ArrayList<M>();
for (final Map<Long, M> cur : entities.values()) {
result.add(cur.values().iterator().next());
}
- return result;
+ return new DefaultPagination<M>(getCount(context), result.iterator());
+ }
+
+ @Override
+ public Pagination<M> get(final Long offset, final Long rowCount, final InternalTenantContext context) {
+ return DefaultPagination.<M>build(offset, rowCount, ImmutableList.<M>copyOf(getAll(context)));
+ }
+
+ @Override
+ public Long getCount(final InternalTenantContext context) {
+ return (long) entities.keySet().size();
}
public void update(final M entity, final InternalCallContext context) {
diff --git a/util/src/test/java/com/ning/billing/util/entity/TestDefaultPagination.java b/util/src/test/java/com/ning/billing/util/entity/TestDefaultPagination.java
new file mode 100644
index 0000000..cfb29f5
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/entity/TestDefaultPagination.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.entity;
+
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.util.UtilTestSuiteNoDB;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestDefaultPagination extends UtilTestSuiteNoDB {
+
+ @Test(groups = "fast", description = "Test Util: Pagination builder tests")
+ public void testDefaultPaginationBuilder() throws Exception {
+ Assert.assertEquals(DefaultPagination.<Integer>build(0L, 0L, ImmutableList.<Integer>of()), expectedOf(0L, 0L, 0L, ImmutableList.<Integer>of()));
+ Assert.assertEquals(DefaultPagination.<Integer>build(0L, 10L, ImmutableList.<Integer>of()), expectedOf(0L, 0L, 0L, ImmutableList.<Integer>of()));
+ Assert.assertEquals(DefaultPagination.<Integer>build(10L, 0L, ImmutableList.<Integer>of()), expectedOf(10L, 0L, 0L, ImmutableList.<Integer>of()));
+ Assert.assertEquals(DefaultPagination.<Integer>build(10L, 10L, ImmutableList.<Integer>of()), expectedOf(10L, 0L, 0L, ImmutableList.<Integer>of()));
+
+ Assert.assertEquals(DefaultPagination.<Integer>build(0L, 0L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(0L, 0L, 5L, ImmutableList.<Integer>of()));
+ Assert.assertEquals(DefaultPagination.<Integer>build(0L, 10L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(0L, 5L, 5L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)));
+ Assert.assertEquals(DefaultPagination.<Integer>build(4L, 10L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(4L, 1L, 5L, ImmutableList.<Integer>of(5)));
+
+ Assert.assertEquals(DefaultPagination.<Integer>build(0L, 3L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(0L, 3L, 5L, ImmutableList.<Integer>of(1, 2, 3)));
+ Assert.assertEquals(DefaultPagination.<Integer>build(1L, 3L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(1L, 3L, 5L, ImmutableList.<Integer>of(2, 3, 4)));
+ Assert.assertEquals(DefaultPagination.<Integer>build(2L, 3L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(2L, 3L, 5L, ImmutableList.<Integer>of(3, 4, 5)));
+ Assert.assertEquals(DefaultPagination.<Integer>build(3L, 3L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(3L, 2L, 5L, ImmutableList.<Integer>of(4, 5)));
+ Assert.assertEquals(DefaultPagination.<Integer>build(4L, 3L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(4L, 1L, 5L, ImmutableList.<Integer>of(5)));
+ Assert.assertEquals(DefaultPagination.<Integer>build(5L, 3L, ImmutableList.<Integer>of(1, 2, 3, 4, 5)), expectedOf(5L, 0L, 5L, ImmutableList.<Integer>of()));
+ }
+
+ private Pagination<Integer> expectedOf(final Long currentOffset, final Long approximateNbResults,
+ final Long approximateTotalNbResults, final List<Integer> delegate) {
+ return new DefaultPagination<Integer>(currentOffset, approximateNbResults, approximateTotalNbResults, delegate.iterator());
+ }
+}
diff --git a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDefinitionDao.java b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDefinitionDao.java
index 9232b1d..592d964 100644
--- a/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDefinitionDao.java
+++ b/util/src/test/java/com/ning/billing/util/tag/dao/MockTagDefinitionDao.java
@@ -23,11 +23,13 @@ import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
-import com.ning.billing.util.api.TagDefinitionApiException;
import com.ning.billing.callcontext.InternalCallContext;
import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.entity.dao.MockEntityDaoBase;
+import com.ning.billing.util.tag.TagDefinition;
-public class MockTagDefinitionDao implements TagDefinitionDao {
+public class MockTagDefinitionDao extends MockEntityDaoBase<TagDefinitionModelDao, TagDefinition, TagDefinitionApiException> implements TagDefinitionDao {
private final Map<String, TagDefinitionModelDao> tags = new ConcurrentHashMap<String, TagDefinitionModelDao>();
@@ -56,11 +58,6 @@ public class MockTagDefinitionDao implements TagDefinitionDao {
}
@Override
- public TagDefinitionModelDao getById(final UUID definitionId, final InternalTenantContext context) {
- return null;
- }
-
- @Override
public List<TagDefinitionModelDao> getByIds(final Collection<UUID> definitionIds, final InternalTenantContext context) {
return null;
}