Skip to content

Commit

Permalink
all: Fixes #505
Browse files Browse the repository at this point in the history
The commit introduced:
* A new ehcache to handle the per-tenant config
* New set of apis to upload/retrieve/get per tenant config
* A new KillbillService to handle configuration (required for cache invalidation)
* Smarter PaymentConfdig and InvoiceConfig binding to correctly handle per-tenant config
  • Loading branch information
sbrossie committed Mar 17, 2016
1 parent 070a97c commit 147bcbb
Show file tree
Hide file tree
Showing 144 changed files with 1,273 additions and 283 deletions.
Expand Up @@ -24,6 +24,7 @@
import org.killbill.billing.util.glue.AuditModule;
import org.killbill.billing.util.glue.CacheModule;
import org.killbill.billing.util.glue.CallContextModule;
import org.killbill.billing.util.glue.ConfigModule;
import org.killbill.billing.util.glue.CustomFieldModule;
import org.killbill.billing.util.glue.TagStoreModule;

Expand All @@ -39,6 +40,7 @@ protected void configure() {

install(new AuditModule(configSource));
install(new CacheModule(configSource));
install(new ConfigModule(configSource));
install(new CallContextModule(configSource));
install(new CustomFieldModule(configSource));
install(new MockTenantModule(configSource));
Expand Down
Expand Up @@ -18,6 +18,8 @@

public interface InvoiceModule {

static final String STATIC_CONFIG = "StaticConfig";

public void installInvoiceUserApi();

public void installInvoicePaymentApi();
Expand Down
Expand Up @@ -41,6 +41,8 @@ public interface CacheInvalidationCallback {

public String getTenantOverdueConfig(InternalTenantContext tenantContext);

public String getTenantConfig(InternalTenantContext tenantContext);

public String getInvoiceTemplate(Locale locale, InternalTenantContext tenantContext);

public String getManualPayInvoiceTemplate(Locale locale, InternalTenantContext tenantContext);
Expand Down
Expand Up @@ -42,13 +42,14 @@
import org.killbill.billing.subscription.glue.DefaultSubscriptionModule;
import org.killbill.billing.tenant.glue.DefaultTenantModule;
import org.killbill.billing.usage.glue.UsageModule;
import org.killbill.billing.util.config.PaymentConfig;
import org.killbill.billing.util.config.definition.PaymentConfig;
import org.killbill.billing.util.email.EmailModule;
import org.killbill.billing.util.email.templates.TemplateModule;
import org.killbill.billing.util.glue.AuditModule;
import org.killbill.billing.util.glue.BroadcastModule;
import org.killbill.billing.util.glue.CacheModule;
import org.killbill.billing.util.glue.CallContextModule;
import org.killbill.billing.util.glue.ConfigModule;
import org.killbill.billing.util.glue.CustomFieldModule;
import org.killbill.billing.util.glue.ExportModule;
import org.killbill.billing.util.glue.GlobalLockerModule;
Expand All @@ -59,8 +60,6 @@
import org.killbill.billing.util.glue.RecordIdModule;
import org.killbill.billing.util.glue.SecurityModule;
import org.killbill.billing.util.glue.TagStoreModule;
import org.killbill.billing.util.security.shiro.realm.KillBillJdbcRealm;
import org.killbill.billing.util.security.shiro.realm.KillBillJndiLdapRealm;

public class BeatrixIntegrationModule extends KillBillModule {

Expand All @@ -78,6 +77,7 @@ protected void configure() {
install(new GuicyKillbillTestWithEmbeddedDBModule(true, configSource));
install(new GlobalLockerModule(configSource));
install(new CacheModule(configSource));
install(new ConfigModule(configSource));
install(new EmailModule(configSource));
install(new CallContextModule(configSource));
install(new TagStoreModule(configSource));
Expand Down
Expand Up @@ -34,7 +34,7 @@
import org.killbill.billing.entitlement.api.DefaultEntitlement;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
import org.killbill.billing.util.config.PaymentConfig;
import org.killbill.billing.util.config.definition.PaymentConfig;

import com.google.inject.Inject;

Expand Down Expand Up @@ -145,7 +145,7 @@ public void testAutoPayOffWithPaymentFailure() throws Exception {
}
assertListenerStatus();

int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays().get(0);
int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays(internalCallContext).get(0);

// MOVE TIME FOR RETRY TO HAPPEN
busHandler.pushExpectedEvents(NextEvent.PAYMENT, NextEvent.INVOICE_PAYMENT);
Expand Down Expand Up @@ -206,7 +206,7 @@ public void testAutoPayOffWithPaymentFailureOn_AUTO_PAY_OFF() throws Exception {
assertListenerStatus();

// RE-ADD AUTO_PAY_OFF to ON
int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays().get(0);
int nbDaysBeforeRetry = paymentConfig.getPaymentFailureRetryDays(internalCallContext).get(0);
add_AUTO_PAY_OFF_Tag(account.getId(), ObjectType.ACCOUNT);

// MOVE TIME FOR RETRY TO HAPPEN -> WILL BE DISCARDED SINCE AUTO_PAY_OFF IS SET
Expand Down
Expand Up @@ -26,15 +26,14 @@
import org.killbill.billing.catalog.api.CatalogService;
import org.killbill.billing.catalog.api.StaticCatalog;
import org.killbill.billing.catalog.caching.CatalogCache;
import org.killbill.billing.catalog.caching.CatalogCacheInvalidationCallback;
import org.killbill.billing.catalog.glue.CatalogModule;
import org.killbill.billing.platform.api.KillbillService;
import org.killbill.billing.platform.api.LifecycleHandlerType;
import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
import org.killbill.billing.tenant.api.TenantInternalApi;
import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
import org.killbill.billing.tenant.api.TenantKV.TenantKey;
import org.killbill.billing.util.config.CatalogConfig;
import org.killbill.billing.util.config.definition.CatalogConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -86,7 +85,7 @@ public synchronized void initialize() throws ServiceException {
tenantInternalApi.initializeCacheInvalidationCallback(TenantKey.CATALOG, cacheInvalidationCallback);
}

@Override
@Override
public String getName() {
return CATALOG_SERVICE_NAME;
}
Expand Down
Expand Up @@ -38,7 +38,7 @@
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
import org.killbill.billing.util.config.CatalogConfig;
import org.killbill.billing.util.config.definition.CatalogConfig;
import org.killbill.billing.util.glue.KillBillModule;
import org.skife.config.ConfigurationObjectFactory;

Expand Down
Expand Up @@ -19,10 +19,8 @@
package org.killbill.billing.catalog;

import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.io.VersionedCatalogLoader;
import org.killbill.billing.platform.api.KillbillService.ServiceException;
import org.killbill.billing.util.config.CatalogConfig;
import org.killbill.clock.DefaultClock;
import org.killbill.billing.util.config.definition.CatalogConfig;
import org.testng.Assert;
import org.testng.annotations.Test;

Expand Down
Expand Up @@ -24,6 +24,7 @@
import org.killbill.billing.mock.glue.MockTenantModule;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.glue.CacheModule;
import org.killbill.billing.util.glue.ConfigModule;

public class TestCatalogModule extends CatalogModule {

Expand All @@ -36,6 +37,7 @@ public void configure() {
super.configure();
install(new MockNonEntityDaoModule(configSource));
install(new CacheModule(configSource));
install(new ConfigModule(configSource));
install(new MockTenantModule(configSource));
install(new MockAccountModule(configSource));
}
Expand Down
Expand Up @@ -26,7 +26,6 @@
import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.util.config.CurrencyConfig;

import com.google.inject.Inject;

Expand Down
Expand Up @@ -26,7 +26,7 @@
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.util.config.CurrencyConfig;
import org.killbill.billing.util.config.definition.CurrencyConfig;

public class DefaultCurrencyConversionApi implements CurrencyConversionApi {

Expand Down
Expand Up @@ -25,7 +25,7 @@
import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.config.CurrencyConfig;
import org.killbill.billing.util.config.definition.CurrencyConfig;
import org.killbill.billing.util.glue.KillBillModule;
import org.skife.config.ConfigurationObjectFactory;

Expand Down
Expand Up @@ -22,6 +22,7 @@
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.glue.CacheModule;
import org.killbill.billing.util.glue.CallContextModule;
import org.killbill.billing.util.glue.ConfigModule;
import org.killbill.billing.util.glue.KillBillShiroAopModule;
import org.killbill.billing.util.glue.KillBillShiroModule;
import org.killbill.billing.util.glue.SecurityModule;
Expand All @@ -40,6 +41,7 @@ public TestEntitlementModule(final KillbillConfigSource configSource) {
protected void configure() {
super.configure();
install(new CacheModule(configSource));
install(new ConfigModule(configSource));
install(new CallContextModule(configSource));
install(new MockTenantModule(configSource));

Expand Down
Expand Up @@ -86,7 +86,7 @@
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.config.InvoiceConfig;
import org.killbill.billing.util.config.definition.InvoiceConfig;
import org.killbill.billing.util.globallocker.LockerType;
import org.killbill.bus.api.PersistentBus;
import org.killbill.bus.api.PersistentBus.EventBusException;
Expand Down Expand Up @@ -413,7 +413,7 @@ private FutureAccountNotifications createNextFutureNotificationDate(final Invoic
}

// If dryRunNotification is enabled we also need to fetch the upcoming PHASE dates (we add SubscriptionNotification with isForInvoiceNotificationTrigger = false)
final boolean isInvoiceNotificationEnabled = invoiceConfig.getDryRunNotificationSchedule().getMillis() > 0;
final boolean isInvoiceNotificationEnabled = invoiceConfig.getDryRunNotificationSchedule(context).getMillis() > 0;
if (isInvoiceNotificationEnabled) {
final Map<UUID, DateTime> upcomingPhasesForSubscriptions = subscriptionApi.getNextFutureEventForSubscriptions(SubscriptionBaseTransitionType.PHASE, context);
for (final UUID cur : upcomingPhasesForSubscriptions.keySet()) {
Expand Down
Expand Up @@ -41,7 +41,7 @@
import org.killbill.billing.util.UUIDs;
import org.killbill.billing.util.callcontext.CallContext;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.InvoiceConfig;
import org.killbill.billing.util.config.definition.InvoiceConfig;
import org.killbill.billing.util.globallocker.LockerType;
import org.killbill.commons.locker.GlobalLock;
import org.killbill.commons.locker.GlobalLocker;
Expand All @@ -54,7 +54,6 @@
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;

public class InvoiceApiHelper {
Expand Down
@@ -0,0 +1,91 @@
/*
* Copyright 2014-2016 Groupon, Inc
* Copyright 2014-2016 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.invoice.config;

import java.lang.reflect.Method;

import javax.inject.Inject;
import javax.inject.Named;

import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.glue.InvoiceModule;
import org.killbill.billing.util.config.definition.InvoiceConfig;
import org.killbill.billing.util.config.tenant.CacheConfig;
import org.killbill.billing.util.config.tenant.MultiTenantConfigBase;
import org.skife.config.TimeSpan;

public class MultiTenantInvoiceConfig extends MultiTenantConfigBase implements InvoiceConfig {

private final InvoiceConfig staticConfig;

@Inject
public MultiTenantInvoiceConfig(@Named(InvoiceModule.STATIC_CONFIG) final InvoiceConfig staticConfig, final CacheConfig cacheConfig) {
super(cacheConfig);
this.staticConfig = staticConfig;
}

@Override
public int getNumberOfMonthsInFuture(final InternalTenantContext tenantContext) {

final Method method = new Object(){}.getClass().getEnclosingMethod();
final String result = getStringTenantConfig(method.getName(), tenantContext);
if (result != null) {
return Integer.parseInt(result);
}
return staticConfig.getNumberOfMonthsInFuture(tenantContext);
}

@Override
public TimeSpan getDryRunNotificationSchedule(final InternalTenantContext tenantContext) {
final Method method = new Object(){}.getClass().getEnclosingMethod();
final String result = getStringTenantConfig(method.getName(), tenantContext);
if (result != null) {
return new TimeSpan(result);
}
return staticConfig.getDryRunNotificationSchedule(tenantContext);
}

@Override
public int getMaxRawUsagePreviousPeriod(final InternalTenantContext tenantContext) {
final Method method = new Object(){}.getClass().getEnclosingMethod();
final String result = getStringTenantConfig(method.getName(), tenantContext);
if (result != null) {
return Integer.parseInt(result);
}
return staticConfig.getMaxRawUsagePreviousPeriod(tenantContext);
}

@Override
public boolean isEmailNotificationsEnabled() {
return staticConfig.isEmailNotificationsEnabled();
}

@Override
public int getMaxGlobalLockRetries() {
return staticConfig.getMaxGlobalLockRetries();
}

@Override
protected Method getConfigStaticMethod(final String methodName) {
try {
return InvoiceConfig.class.getMethod(methodName, InternalTenantContext.class);
} catch (final NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
Expand Up @@ -50,7 +50,7 @@
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
import org.killbill.billing.util.config.InvoiceConfig;
import org.killbill.billing.util.config.definition.InvoiceConfig;
import org.killbill.billing.util.dao.NonEntityDao;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
Expand Down Expand Up @@ -825,7 +825,7 @@ public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFa
private void notifyOfFutureBillingEvents(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final UUID accountId,
final FutureAccountNotifications callbackDateTimePerSubscriptions, final InternalCallContext internalCallContext) {

final long dryRunNotificationTime = invoiceConfig.getDryRunNotificationSchedule().getMillis();
final long dryRunNotificationTime = invoiceConfig.getDryRunNotificationSchedule(internalCallContext).getMillis();
final boolean isInvoiceNotificationEnabled = dryRunNotificationTime > 0;
for (final UUID subscriptionId : callbackDateTimePerSubscriptions.getNotifications().keySet()) {
final List<SubscriptionNotification> callbackDateTimeUTC = callbackDateTimePerSubscriptions.getNotifications().get(subscriptionId);
Expand Down
Expand Up @@ -30,14 +30,15 @@
import org.killbill.billing.ErrorCode;
import org.killbill.billing.account.api.ImmutableAccountData;
import org.killbill.billing.callcontext.InternalCallContext;
import org.killbill.billing.callcontext.InternalTenantContext;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.invoice.api.InvoiceItem;
import org.killbill.billing.invoice.generator.InvoiceWithMetadata.SubscriptionFutureNotificationDates;
import org.killbill.billing.invoice.model.DefaultInvoice;
import org.killbill.billing.junction.BillingEventSet;
import org.killbill.billing.util.config.InvoiceConfig;
import org.killbill.billing.util.config.definition.InvoiceConfig;
import org.killbill.clock.Clock;

import com.google.common.collect.ImmutableMap;
Expand Down Expand Up @@ -71,7 +72,7 @@ public InvoiceWithMetadata generateInvoice(final ImmutableAccountData account, @
return new InvoiceWithMetadata(null, ImmutableMap.<UUID, SubscriptionFutureNotificationDates>of());
}

validateTargetDate(targetDate);
validateTargetDate(targetDate, context);
final LocalDate adjustedTargetDate = adjustTargetDate(existingInvoices, targetDate);

final LocalDate invoiceDate = context.toLocalDate(clock.getUTCNow());
Expand All @@ -89,8 +90,8 @@ public InvoiceWithMetadata generateInvoice(final ImmutableAccountData account, @
return new InvoiceWithMetadata(invoice.getInvoiceItems().isEmpty() ? null : invoice, perSubscriptionFutureNotificationDates);
}

private void validateTargetDate(final LocalDate targetDate) throws InvoiceApiException {
final int maximumNumberOfMonths = config.getNumberOfMonthsInFuture();
private void validateTargetDate(final LocalDate targetDate, final InternalTenantContext context) throws InvoiceApiException {
final int maximumNumberOfMonths = config.getNumberOfMonthsInFuture(context);

if (Months.monthsBetween(clock.getUTCToday(), targetDate).getMonths() > maximumNumberOfMonths) {
throw new InvoiceApiException(ErrorCode.INVOICE_TARGET_DATE_TOO_FAR_IN_THE_FUTURE, targetDate.toString());
Expand Down

0 comments on commit 147bcbb

Please sign in to comment.