Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<!--
~ Copyright 2010-2014 Ning, Inc.
~ Copyright 2014-2020 Groupon, Inc
~ Copyright 2020-2022 Equinix, Inc
~ Copyright 2014-2022 The Billing Project, LLC
~ Copyright 2020-2023 Equinix, Inc
~ Copyright 2014-2023 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
Expand Down Expand Up @@ -193,6 +193,11 @@
<artifactId>killbill-platform-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.kill-bill.billing.plugin</groupId>
<artifactId>killbill-plugin-api-currency</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.kill-bill.billing.plugin</groupId>
<artifactId>killbill-plugin-api-notification</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import javax.servlet.Servlet;
import javax.servlet.http.HttpServlet;

import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
import org.killbill.billing.osgi.api.Healthcheck;
import org.killbill.billing.osgi.api.OSGIPluginProperties;
import org.killbill.billing.osgi.libs.killbill.KillbillActivatorBase;
Expand Down Expand Up @@ -58,6 +59,7 @@
import org.killbill.notificationq.api.NotificationQueueConfig;
import org.killbill.notificationq.dao.NotificationEventModelDao;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import org.skife.config.ConfigurationObjectFactory;
import org.skife.jdbi.v2.DBI;
import org.slf4j.Logger;
Expand All @@ -73,8 +75,11 @@ public class AnalyticsActivator extends KillbillActivatorBase {

public static final String PLUGIN_NAME = "killbill-analytics";
public static final String ANALYTICS_QUEUE_SERVICE = "AnalyticsService";

private static final Logger logger = LoggerFactory.getLogger(AnalyticsActivator.class);

private AnalyticsConfigurationHandler analyticsConfigurationHandler;
private ServiceTracker<CurrencyPluginApi, CurrencyPluginApi> currencyPluginApiServiceTracker;
private AnalyticsListener analyticsListener;
private JobsScheduler jobsScheduler;
private ReportsUserApi reportsUserApi;
Expand Down Expand Up @@ -126,10 +131,15 @@ public void start(final BundleContext context) throws Exception {
locker = new MemoryGlobalLocker();
break;
}

currencyPluginApiServiceTracker = new ServiceTracker<>(context, CurrencyPluginApi.class, null);
currencyPluginApiServiceTracker.open();

analyticsListener = new AnalyticsListener(roOSGIkillbillAPI,
dataSource,
metricRegistry,
configProperties,
currencyPluginApiServiceTracker,
executor,
locker,
killbillClock,
Expand All @@ -140,7 +150,7 @@ public void start(final BundleContext context) throws Exception {

final ReportsConfiguration reportsConfiguration = new ReportsConfiguration(dataSource, metricRegistry, jobsScheduler);

final AnalyticsUserApi analyticsUserApi = new AnalyticsUserApi(roOSGIkillbillAPI, dataSource, metricRegistry, configProperties, executor, killbillClock, analyticsConfigurationHandler, analyticsListener);
final AnalyticsUserApi analyticsUserApi = new AnalyticsUserApi(roOSGIkillbillAPI, dataSource, metricRegistry, configProperties, currencyPluginApiServiceTracker, executor, killbillClock, analyticsConfigurationHandler, analyticsListener);
reportsUserApi = new ReportsUserApi(roOSGIkillbillAPI, dataSource, metricRegistry, configProperties, dbEngine, reportsConfiguration, jobsScheduler, analyticsConfigurationHandler);

final AnalyticsHealthcheck healthcheck = new AnalyticsHealthcheck(analyticsListener, jobsScheduler);
Expand Down Expand Up @@ -175,6 +185,9 @@ public void stop(final BundleContext context) throws Exception {
if (jobsScheduler != null) {
jobsScheduler.shutdownNow();
}
if (currencyPluginApiServiceTracker != null) {
currencyPluginApiServiceTracker.close();
}
if (analyticsListener != null) {
// Little bit of subtlety here, which is queue implementation dependent: only the second time
// the queue is asked to stop that it will actually go through the shutdown sequence
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import org.joda.time.DateTime;
import org.killbill.billing.ObjectType;
import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
import org.killbill.billing.invoice.api.Invoice;
import org.killbill.billing.invoice.api.InvoiceApiException;
import org.killbill.billing.notification.plugin.api.ExtBusEvent;
Expand Down Expand Up @@ -65,6 +66,7 @@
import org.killbill.notificationq.api.NotificationQueue;
import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueAlreadyExists;
import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueHandler;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -93,6 +95,7 @@ public class AnalyticsListener implements OSGIKillbillEventDispatcher.OSGIKillbi
private final BusinessAccountTransitionDao bosDao;
private final BusinessFieldDao bFieldDao;
private final AllBusinessObjectsDao allBusinessObjectsDao;
private final ServiceTracker<CurrencyPluginApi, CurrencyPluginApi> currencyPluginApiServiceTracker;
private final CurrencyConversionDao currencyConversionDao;
private final NotificationQueue jobQueue;
private final GlobalLocker locker;
Expand All @@ -103,13 +106,15 @@ public AnalyticsListener(final OSGIKillbillAPI osgiKillbillAPI,
final OSGIKillbillDataSource osgiKillbillDataSource,
final OSGIMetricRegistry metricRegistry,
final OSGIConfigPropertiesService osgiConfigPropertiesService,
final ServiceTracker<CurrencyPluginApi, CurrencyPluginApi> currencyPluginApiServiceTracker,
final Executor executor,
final GlobalLocker locker,
final Clock clock,
final AnalyticsConfigurationHandler analyticsConfigurationHandler,
final DefaultNotificationQueueService notificationQueueService) throws NotificationQueueAlreadyExists {
this.osgiKillbillAPI = osgiKillbillAPI;
this.osgiConfigPropertiesService = osgiConfigPropertiesService;
this.currencyPluginApiServiceTracker = currencyPluginApiServiceTracker;
this.locker = locker;
this.clock = clock;
this.analyticsConfigurationHandler = analyticsConfigurationHandler;
Expand Down Expand Up @@ -346,7 +351,7 @@ private void handleAnalyticsJobWithLock(final AnalyticsJob job) throws Analytics
}

final CallContext callContext = new AnalyticsCallContext(job, clock);
final BusinessContextFactory businessContextFactory = new BusinessContextFactory(job.getAccountId(), callContext, currencyConversionDao, osgiKillbillAPI, osgiConfigPropertiesService, clock, analyticsConfigurationHandler);
final BusinessContextFactory businessContextFactory = new BusinessContextFactory(job.getAccountId(), callContext, currencyPluginApiServiceTracker, currencyConversionDao, osgiKillbillAPI, osgiConfigPropertiesService, clock, analyticsConfigurationHandler);

// Pre 7.2.4, the group wasn't stored in the event
final Group group = MoreObjects.firstNonNull(job.getGroup(), AnalyticsJobHierarchy.fromEventType(job.getEventType()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import org.killbill.billing.ObjectType;
import org.killbill.billing.account.api.Account;
import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
import org.killbill.billing.notification.plugin.api.ExtBusEventType;
import org.killbill.billing.osgi.libs.killbill.OSGIConfigPropertiesService;
import org.killbill.billing.osgi.libs.killbill.OSGIKillbillAPI;
Expand Down Expand Up @@ -55,6 +56,7 @@
import org.killbill.billing.util.callcontext.TenantContext;
import org.killbill.billing.util.entity.Pagination;
import org.killbill.clock.Clock;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -68,26 +70,28 @@ public class AnalyticsUserApi {
private final AnalyticsConfigurationHandler analyticsConfigurationHandler;
private final AnalyticsDao analyticsDao;
private final AllBusinessObjectsDao allBusinessObjectsDao;
private final ServiceTracker<CurrencyPluginApi, CurrencyPluginApi> currencyPluginApiServiceTracker;
private final CurrencyConversionDao currencyConversionDao;
private final AnalyticsListener analyticsListener;

public AnalyticsUserApi(final OSGIKillbillAPI osgiKillbillAPI,
final OSGIKillbillDataSource osgiKillbillDataSource,
final OSGIMetricRegistry metricRegistry,
final OSGIConfigPropertiesService osgiConfigPropertiesService,
final ServiceTracker<CurrencyPluginApi, CurrencyPluginApi> currencyPluginApiServiceTracker,
final Executor executor,
final Clock clock,
final AnalyticsConfigurationHandler analyticsConfigurationHandler,
final AnalyticsListener analyticsListener) {
this.osgiKillbillAPI = osgiKillbillAPI;
this.osgiConfigPropertiesService = osgiConfigPropertiesService;
this.currencyPluginApiServiceTracker = currencyPluginApiServiceTracker;
this.clock = clock;
this.analyticsConfigurationHandler = analyticsConfigurationHandler;
this.analyticsDao = new AnalyticsDao(osgiKillbillAPI, osgiKillbillDataSource, metricRegistry);
this.allBusinessObjectsDao = new AllBusinessObjectsDao(osgiKillbillDataSource, metricRegistry, executor);
this.currencyConversionDao = new CurrencyConversionDao(osgiKillbillDataSource, metricRegistry);
this.analyticsListener = analyticsListener;

}

public BusinessSnapshot getBusinessSnapshot(final UUID accountId, final TenantContext context) {
Expand Down Expand Up @@ -124,7 +128,7 @@ public BusinessSnapshot getBusinessSnapshot(final UUID accountId, final TenantCo
}

public void rebuildAnalyticsForAccount(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
final BusinessContextFactory businessContextFactory = new BusinessContextFactory(accountId, context, currencyConversionDao, osgiKillbillAPI, osgiConfigPropertiesService, clock, analyticsConfigurationHandler);
final BusinessContextFactory businessContextFactory = new BusinessContextFactory(accountId, context, currencyPluginApiServiceTracker, currencyConversionDao, osgiKillbillAPI, osgiConfigPropertiesService, clock, analyticsConfigurationHandler);
logger.info("Starting Analytics refresh for account {}", businessContextFactory.getAccountId());
// TODO Should we take the account lock?
allBusinessObjectsDao.update(businessContextFactory);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*
* Copyright 2010-2014 Ning, Inc.
* Copyright 2014-2020 Groupon, Inc
* Copyright 2020-2020 Equinix, Inc
* Copyright 2014-2020 The Billing Project, LLC
* Copyright 2020-2023 Equinix, Inc
* Copyright 2014-2023 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
Expand Down Expand Up @@ -31,6 +31,7 @@
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.VersionedCatalog;
import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
import org.killbill.billing.entitlement.api.BlockingState;
import org.killbill.billing.entitlement.api.Subscription;
import org.killbill.billing.entitlement.api.SubscriptionBundle;
Expand All @@ -54,6 +55,7 @@
import org.killbill.billing.util.tag.Tag;
import org.killbill.billing.util.tag.TagDefinition;
import org.killbill.clock.Clock;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -116,12 +118,13 @@ public class BusinessContextFactory extends BusinessFactoryBase {

public BusinessContextFactory(final UUID accountId,
final CallContext callContext,
final ServiceTracker<CurrencyPluginApi, CurrencyPluginApi> currencyPluginApiServiceTracker,
final CurrencyConversionDao currencyConversionDao,
final OSGIKillbillAPI osgiKillbillAPI,
final OSGIConfigPropertiesService osgiConfigPropertiesService,
final Clock clock,
final AnalyticsConfigurationHandler analyticsConfigurationHandler) throws AnalyticsRefreshException {
super(accountId, callContext, currencyConversionDao, osgiKillbillAPI, osgiConfigPropertiesService, clock, analyticsConfigurationHandler);
super(accountId, callContext, currencyPluginApiServiceTracker, currencyConversionDao, osgiKillbillAPI, osgiConfigPropertiesService, clock, analyticsConfigurationHandler);
this.analyticsConfigurationHandler = analyticsConfigurationHandler;

// Always needed
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/*
* Copyright 2010-2014 Ning, Inc.
* Copyright 2014-2020 Groupon, Inc
* Copyright 2020-2020 Equinix, Inc
* Copyright 2014-2020 The Billing Project, LLC
* Copyright 2020-2023 Equinix, Inc
* Copyright 2014-2023 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
Expand Down Expand Up @@ -38,9 +38,11 @@
import org.killbill.billing.account.api.AccountUserApi;
import org.killbill.billing.catalog.api.CatalogApiException;
import org.killbill.billing.catalog.api.CatalogUserApi;
import org.killbill.billing.catalog.api.Currency;
import org.killbill.billing.catalog.api.Plan;
import org.killbill.billing.catalog.api.PlanPhase;
import org.killbill.billing.catalog.api.VersionedCatalog;
import org.killbill.billing.currency.plugin.api.CurrencyPluginApi;
import org.killbill.billing.entitlement.api.BlockingState;
import org.killbill.billing.entitlement.api.EntitlementApiException;
import org.killbill.billing.entitlement.api.Subscription;
Expand All @@ -65,7 +67,10 @@
import org.killbill.billing.plugin.analytics.api.core.AnalyticsConfigurationHandler;
import org.killbill.billing.plugin.analytics.dao.CurrencyConversionDao;
import org.killbill.billing.plugin.analytics.dao.model.BusinessModelDaoBase.ReportGroup;
import org.killbill.billing.plugin.analytics.utils.CurrencyConversions;
import org.killbill.billing.plugin.analytics.utils.CurrencyConverter;
import org.killbill.billing.plugin.analytics.utils.CurrencyPluginApiCurrencyConversions;
import org.killbill.billing.plugin.analytics.utils.StaticCurrencyConversions;
import org.killbill.billing.util.api.AuditLevel;
import org.killbill.billing.util.api.AuditUserApi;
import org.killbill.billing.util.api.CustomFieldUserApi;
Expand All @@ -82,6 +87,7 @@
import org.killbill.billing.util.tag.Tag;
import org.killbill.billing.util.tag.TagDefinition;
import org.killbill.clock.Clock;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -108,17 +114,20 @@ public abstract class BusinessFactoryBase {

private final boolean highCardinalityAccount;
private final String referenceCurrency;
private final ServiceTracker<CurrencyPluginApi, CurrencyPluginApi> currencyPluginApiServiceTracker;
private final CurrencyConversionDao currencyConversionDao;

public BusinessFactoryBase(final UUID accountId,
final CallContext callContext,
final ServiceTracker<CurrencyPluginApi, CurrencyPluginApi> currencyPluginApiServiceTracker,
final CurrencyConversionDao currencyConversionDao,
final OSGIKillbillAPI osgiKillbillAPI,
final OSGIConfigPropertiesService osgiConfigPropertiesService,
final Clock clock,
final AnalyticsConfigurationHandler analyticsConfigurationHandler) {
this.accountId = accountId;
this.callContext = callContext;
this.currencyPluginApiServiceTracker = currencyPluginApiServiceTracker;
this.osgiKillbillAPI = osgiKillbillAPI;
this.clock = clock;
this.referenceCurrency = MoreObjects.firstNonNull(Strings.emptyToNull(osgiConfigPropertiesService.getString(ANALYTICS_REFERENCE_CURRENCY_PROPERTY)), "USD");
Expand All @@ -136,7 +145,16 @@ public boolean highCardinalityAccount() {
//

protected CurrencyConverter getCurrencyConverter() {
return new CurrencyConverter(clock, referenceCurrency, currencyConversionDao.getCurrencyConversions(referenceCurrency));
return new CurrencyConverter(clock, referenceCurrency, getCurrencyConversions());
}

protected CurrencyConversions getCurrencyConversions() {
final CurrencyPluginApi currencyPluginApi = currencyPluginApiServiceTracker.getService();
if (currencyPluginApi == null) {
return new StaticCurrencyConversions(currencyConversionDao.getCurrencyConversions(referenceCurrency));
} else {
return new CurrencyPluginApiCurrencyConversions(currencyPluginApi, Currency.valueOf(referenceCurrency));
}
}

//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2020-2023 Equinix, Inc
* Copyright 2014-2023 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.plugin.analytics.utils;

import java.math.BigDecimal;

import org.joda.time.LocalDate;

public interface CurrencyConversions {

BigDecimal getConvertedValue(BigDecimal value, String currency, LocalDate effectiveDate);
}
Loading