Commit
…CD (updated only once). The code also refactors the account API to make sure any path (recordId, id, externalKey) whether from public or private API will end up setting the cache.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,52 +22,72 @@ | |
import javax.inject.Inject; | ||
|
||
import org.killbill.billing.ErrorCode; | ||
import org.killbill.billing.ObjectType; | ||
import org.killbill.billing.account.api.Account; | ||
import org.killbill.billing.account.api.AccountApiException; | ||
import org.killbill.billing.account.api.AccountEmail; | ||
import org.killbill.billing.account.api.AccountInternalApi; | ||
import org.killbill.billing.account.api.DefaultAccount; | ||
import org.killbill.billing.account.api.DefaultAccountEmail; | ||
import org.killbill.billing.account.api.DefaultImmutableAccountData; | ||
import org.killbill.billing.account.api.DefaultMutableAccountData; | ||
import org.killbill.billing.account.api.ImmutableAccountData; | ||
import org.killbill.billing.account.api.MutableAccountData; | ||
import org.killbill.billing.account.api.user.DefaultAccountApiBase; | ||
import org.killbill.billing.account.dao.AccountDao; | ||
import org.killbill.billing.account.dao.AccountEmailModelDao; | ||
import org.killbill.billing.account.dao.AccountModelDao; | ||
import org.killbill.billing.callcontext.InternalCallContext; | ||
import org.killbill.billing.callcontext.InternalTenantContext; | ||
import org.killbill.billing.util.cache.AccountBCDCacheLoader; | ||
import org.killbill.billing.util.cache.Cachable.CacheType; | ||
import org.killbill.billing.util.cache.CacheController; | ||
import org.killbill.billing.util.cache.CacheControllerDispatcher; | ||
import org.killbill.billing.util.cache.CacheLoaderArgument; | ||
import org.killbill.billing.util.cache.ImmutableAccountCacheLoader.LoaderCallback; | ||
import org.killbill.billing.util.dao.NonEntityDao; | ||
|
||
import com.google.common.base.Function; | ||
import com.google.common.collect.Collections2; | ||
import com.google.common.collect.ImmutableList; | ||
|
||
public class DefaultAccountInternalApi implements AccountInternalApi { | ||
public class DefaultAccountInternalApi extends DefaultAccountApiBase implements AccountInternalApi { | ||
|
||
private final AccountDao accountDao; | ||
private final CacheControllerDispatcher cacheControllerDispatcher; | ||
private final CacheController accountCacheController; | ||
private final CacheController bcdCacheController; | ||
private final NonEntityDao nonEntityDao; | ||
|
||
@Inject | ||
public DefaultAccountInternalApi(final AccountDao accountDao) { | ||
public DefaultAccountInternalApi(final AccountDao accountDao, | ||
final NonEntityDao nonEntityDao, | ||
final CacheControllerDispatcher cacheControllerDispatcher) { | ||
super(accountDao, nonEntityDao, cacheControllerDispatcher); | ||
this.accountDao = accountDao; | ||
this.nonEntityDao = nonEntityDao; | ||
this.cacheControllerDispatcher = cacheControllerDispatcher; | ||
this.accountCacheController = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_IMMUTABLE); | ||
this.bcdCacheController = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_BCD); | ||
} | ||
|
||
@Override | ||
public Account getAccountById(final UUID accountId, final InternalTenantContext context) throws AccountApiException { | ||
final AccountModelDao account = accountDao.getById(accountId, context); | ||
if (account == null) { | ||
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId); | ||
} | ||
return new DefaultAccount(account); | ||
return super.getAccountById(accountId, context); | ||
} | ||
|
||
@Override | ||
public Account getAccountByKey(final String key, final InternalTenantContext context) throws AccountApiException { | ||
return super.getAccountByKey(key, context); | ||
} | ||
|
||
@Override | ||
public Account getAccountByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException { | ||
final AccountModelDao accountModelDao = getAccountModelDaoByRecordId(recordId, context); | ||
return new DefaultAccount(accountModelDao); | ||
return super.getAccountByRecordId(recordId, context); | ||
} | ||
|
||
@Override | ||
public void updateBCD(final String externalKey, final int bcd, | ||
final InternalCallContext context) throws AccountApiException { | ||
final InternalCallContext context) throws AccountApiException { | ||
final Account currentAccount = getAccountByKey(externalKey, context); | ||
if (currentAccount == null) { | ||
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, externalKey); | ||
|
@@ -76,13 +96,15 @@ public void updateBCD(final String externalKey, final int bcd, | |
final MutableAccountData mutableAccountData = currentAccount.toMutableAccountData(); | ||
mutableAccountData.setBillCycleDayLocal(bcd); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
sbrossie
Author
Member
|
||
final AccountModelDao accountToUpdate = new AccountModelDao(currentAccount.getId(), mutableAccountData); | ||
bcdCacheController.putIfAbsent(currentAccount.getId(), new Integer(bcd)); | ||
accountDao.update(accountToUpdate, context); | ||
} | ||
|
||
@Override | ||
public int getBCD(final UUID accountId, final InternalTenantContext context) throws AccountApiException { | ||
final Account account = getAccountById(accountId, context); | ||
return account.getBillCycleDayLocal(); | ||
final CacheLoaderArgument arg = createBCDCacheLoaderArgument(context); | ||
final Integer result = (Integer) bcdCacheController.get(accountId, arg); | ||
return result != null ? result : DefaultMutableAccountData.DEFAULT_BILLING_CYCLE_DAY_LOCAL; | ||
} | ||
|
||
@Override | ||
|
@@ -97,15 +119,6 @@ public AccountEmail apply(final AccountEmailModelDao input) { | |
})); | ||
} | ||
|
||
@Override | ||
public Account getAccountByKey(final String key, final InternalTenantContext context) throws AccountApiException { | ||
final AccountModelDao accountModelDao = accountDao.getAccountByKey(key, context); | ||
if (accountModelDao == null) { | ||
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, key); | ||
} | ||
return new DefaultAccount(accountModelDao); | ||
} | ||
|
||
@Override | ||
public void removePaymentMethod(final UUID accountId, final InternalCallContext context) throws AccountApiException { | ||
updatePaymentMethod(accountId, null, context); | ||
|
@@ -125,20 +138,14 @@ public UUID getByRecordId(final Long recordId, final InternalTenantContext conte | |
|
||
@Override | ||
public ImmutableAccountData getImmutableAccountDataById(final UUID accountId, final InternalTenantContext context) throws AccountApiException { | ||
final Account account = getAccountById(accountId, context); | ||
return new DefaultImmutableAccountData(account); | ||
} | ||
|
||
@Override | ||
public ImmutableAccountData getImmutableAccountDataByKey(final String key, final InternalTenantContext context) throws AccountApiException { | ||
final Account account = getAccountByKey(key, context); | ||
return new DefaultImmutableAccountData(account); | ||
final Long recordId = nonEntityDao.retrieveRecordIdFromObject(accountId, ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID)); | ||
return getImmutableAccountDataByRecordId(recordId, context); | ||
} | ||
|
||
@Override | ||
public ImmutableAccountData getImmutableAccountDataByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException { | ||
final Account account = getAccountByRecordId(recordId, context); | ||
return new DefaultImmutableAccountData(account); | ||
final CacheLoaderArgument arg = createImmutableAccountCacheLoaderArgument(context); | ||
return (ImmutableAccountData) accountCacheController.get(recordId, arg); | ||
} | ||
|
||
private AccountModelDao getAccountModelDaoByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException { | ||
|
@@ -149,5 +156,36 @@ private AccountModelDao getAccountModelDaoByRecordId(final Long recordId, final | |
return accountModelDao; | ||
} | ||
|
||
private int getBCDInternal(final UUID accountId, final InternalTenantContext context) { | ||
final Long bcd = accountDao.getAccountBCD(accountId, context); | ||
return bcd != null ? bcd.intValue() : DefaultMutableAccountData.DEFAULT_BILLING_CYCLE_DAY_LOCAL; | ||
} | ||
|
||
private CacheLoaderArgument createImmutableAccountCacheLoaderArgument(final InternalTenantContext context) { | ||
final LoaderCallback loaderCallback = new LoaderCallback() { | ||
@Override | ||
public Object loadAccount(final Long recordId, final InternalTenantContext context) { | ||
final Account account = getAccountByRecordIdInternal(recordId, context); | ||
return account != null ? new DefaultImmutableAccountData(account) : null; | ||
} | ||
}; | ||
final Object[] args = new Object[1]; | ||
args[0] = loaderCallback; | ||
final ObjectType irrelevant = null; | ||
return new CacheLoaderArgument(irrelevant, args, context); | ||
} | ||
|
||
private CacheLoaderArgument createBCDCacheLoaderArgument(final InternalTenantContext context) { | ||
final AccountBCDCacheLoader.LoaderCallback loaderCallback = new AccountBCDCacheLoader.LoaderCallback() { | ||
@Override | ||
public Object loadAccountBCD(final UUID accountId, final InternalTenantContext context) { | ||
int bcd = getBCDInternal(accountId, context); | ||
return new Integer(bcd); | ||
} | ||
}; | ||
final Object[] args = new Object[1]; | ||
args[0] = loaderCallback; | ||
final ObjectType irrelevant = null; | ||
return new CacheLoaderArgument(irrelevant, args, context); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
* Copyright 2014-2015 Groupon, Inc | ||
* Copyright 2014-2015 The Billing Project, LLC | ||
* | ||
* The Billing Project 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 org.killbill.billing.account.api.user; | ||
|
||
import java.util.UUID; | ||
|
||
import org.killbill.billing.ErrorCode; | ||
import org.killbill.billing.ObjectType; | ||
import org.killbill.billing.account.api.Account; | ||
import org.killbill.billing.account.api.AccountApiException; | ||
import org.killbill.billing.account.api.DefaultAccount; | ||
import org.killbill.billing.account.api.DefaultImmutableAccountData; | ||
import org.killbill.billing.account.dao.AccountDao; | ||
import org.killbill.billing.account.dao.AccountModelDao; | ||
import org.killbill.billing.callcontext.InternalTenantContext; | ||
import org.killbill.billing.util.cache.Cachable.CacheType; | ||
import org.killbill.billing.util.cache.CacheController; | ||
import org.killbill.billing.util.cache.CacheControllerDispatcher; | ||
import org.killbill.billing.util.dao.NonEntityDao; | ||
|
||
public class DefaultAccountApiBase { | ||
|
||
private final AccountDao accountDao; | ||
private final CacheControllerDispatcher cacheControllerDispatcher; | ||
private final CacheController accountCacheController; | ||
private final NonEntityDao nonEntityDao; | ||
|
||
public DefaultAccountApiBase(final AccountDao accountDao, | ||
final NonEntityDao nonEntityDao, | ||
final CacheControllerDispatcher cacheControllerDispatcher) { | ||
this.accountDao = accountDao; | ||
this.nonEntityDao = nonEntityDao; | ||
this.cacheControllerDispatcher = cacheControllerDispatcher; | ||
this.accountCacheController = cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_IMMUTABLE); | ||
} | ||
|
||
protected Account getAccountById(final UUID accountId, final InternalTenantContext context) throws AccountApiException { | ||
final Long recordId = nonEntityDao.retrieveRecordIdFromObject(accountId, ObjectType.ACCOUNT, cacheControllerDispatcher.getCacheController(CacheType.RECORD_ID)); | ||
final Account account = getAccountByRecordIdInternal(recordId, context); | ||
if (account == null) { | ||
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, accountId); | ||
} | ||
accountCacheController.putIfAbsent(accountId, new DefaultImmutableAccountData(account)); | ||
return account; | ||
} | ||
|
||
protected Account getAccountByKey(final String key, final InternalTenantContext context) throws AccountApiException { | ||
final AccountModelDao accountModelDao = accountDao.getAccountByKey(key, context); | ||
if (accountModelDao == null) { | ||
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, key); | ||
} | ||
final Account account = new DefaultAccount(accountModelDao); | ||
accountCacheController.putIfAbsent(account.getId(), new DefaultImmutableAccountData(account)); | ||
return account; | ||
} | ||
|
||
protected Account getAccountByRecordId(final Long recordId, final InternalTenantContext context) throws AccountApiException { | ||
final Account account = getAccountByRecordIdInternal(recordId, context); | ||
if (account == null) { | ||
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_RECORD_ID, recordId); | ||
} | ||
return account; | ||
} | ||
|
||
protected Account getAccountByRecordIdInternal(final Long recordId, final InternalTenantContext context) { | ||
final AccountModelDao accountModelDao = accountDao.getByRecordId(recordId, context); | ||
return accountModelDao != null ? new DefaultAccount(accountModelDao) : null; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,9 +32,12 @@ | |
import org.killbill.billing.account.dao.AccountDao; | ||
import org.killbill.billing.account.dao.AccountEmailModelDao; | ||
import org.killbill.billing.account.dao.AccountModelDao; | ||
import org.killbill.billing.callcontext.InternalTenantContext; | ||
import org.killbill.billing.util.cache.CacheControllerDispatcher; | ||
import org.killbill.billing.util.callcontext.CallContext; | ||
import org.killbill.billing.util.callcontext.InternalCallContextFactory; | ||
import org.killbill.billing.util.callcontext.TenantContext; | ||
import org.killbill.billing.util.dao.NonEntityDao; | ||
import org.killbill.billing.util.entity.Pagination; | ||
import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder; | ||
|
||
|
@@ -45,17 +48,35 @@ | |
|
||
import static org.killbill.billing.util.entity.dao.DefaultPaginationHelper.getEntityPaginationNoException; | ||
|
||
public class DefaultAccountUserApi implements AccountUserApi { | ||
public class DefaultAccountUserApi extends DefaultAccountApiBase implements AccountUserApi { | ||
|
||
private final InternalCallContextFactory internalCallContextFactory; | ||
private final AccountDao accountDao; | ||
|
||
@Inject | ||
public DefaultAccountUserApi(final InternalCallContextFactory internalCallContextFactory, final AccountDao accountDao) { | ||
public DefaultAccountUserApi(final AccountDao accountDao, | ||
final NonEntityDao nonEntityDao, | ||
final CacheControllerDispatcher cacheControllerDispatcher, | ||
final InternalCallContextFactory internalCallContextFactory) { | ||
super(accountDao, nonEntityDao, cacheControllerDispatcher); | ||
this.internalCallContextFactory = internalCallContextFactory; | ||
this.accountDao = accountDao; | ||
} | ||
|
||
|
||
@Override | ||
public Account getAccountByKey(final String key, final TenantContext context) throws AccountApiException { | ||
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context); | ||
return getAccountByKey(key, internalTenantContext); | ||
} | ||
|
||
@Override | ||
public Account getAccountById(final UUID id, final TenantContext context) throws AccountApiException { | ||
final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context); | ||
return getAccountById(id, internalTenantContext); | ||
} | ||
|
||
|
||
@Override | ||
public Account createAccount(final AccountData data, final CallContext context) throws AccountApiException { | ||
// Not transactional, but there is a db constraint on that column | ||
|
@@ -69,25 +90,6 @@ public Account createAccount(final AccountData data, final CallContext context) | |
return new DefaultAccount(account); | ||
This comment has been minimized.
Sorry, something went wrong.
pierre
Member
|
||
} | ||
|
||
@Override | ||
public Account getAccountByKey(final String key, final TenantContext context) throws AccountApiException { | ||
final AccountModelDao account = accountDao.getAccountByKey(key, internalCallContextFactory.createInternalTenantContext(context)); | ||
if (account == null) { | ||
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_KEY, key); | ||
} | ||
|
||
return new DefaultAccount(account); | ||
} | ||
|
||
@Override | ||
public Account getAccountById(final UUID id, final TenantContext context) throws AccountApiException { | ||
final AccountModelDao account = accountDao.getById(id, internalCallContextFactory.createInternalTenantContext(context)); | ||
if (account == null) { | ||
throw new AccountApiException(ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_ID, id); | ||
} | ||
|
||
return new DefaultAccount(account); | ||
} | ||
|
||
@Override | ||
public Pagination<Account> searchAccounts(final String searchKey, final Long offset, final Long limit, final TenantContext context) { | ||
|
This section of code might be a bit dangerous.
Previously, junction would call
updateAccount
which would callmergeWithDelegate
. The implementation has a safety check to ensure the BCD isn't updated. Now, we by-pass this check here, so we need to make sureDefaultInternalBillingApi
knows when to update or not (using the magic0
value?).If not, we could be in a really weird state as if the account was created with the BCD set,
putIfAbsent
below wouldn't update the cache and we would be out-of-sync with the db...