killbill-memoizeit

Initial version for profiling code

7/29/2014 10:03:17 PM

Changes

jaxrs/pom.xml 1(+0 -1)

payment/pom.xml 1(+0 -1)

pom.xml 2(+1 -1)

util/pom.xml 4(+4 -0)

Details

jaxrs/pom.xml 1(+0 -1)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index 12fe841..716d1ed 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -88,7 +88,6 @@
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-platform-base</artifactId>
-            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/ProfilingDataJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/ProfilingDataJson.java
new file mode 100644
index 0000000..96d6096
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/ProfilingDataJson.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.jaxrs.json;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import org.killbill.billing.platform.profiling.ProfilingData;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class ProfilingDataJson {
+
+    private final Map<String, List<Long>> rawData;
+    private final String createdUri;
+
+    @JsonCreator
+    public ProfilingDataJson(@JsonProperty("createdUri") final String createdUri,
+            @JsonProperty("rawData") final Map<String, List<Long>> rawData) {
+        this.createdUri = createdUri;
+        this.rawData = rawData;
+    }
+
+    public ProfilingDataJson(final ProfilingData data, final URI uri) {
+        this(uri.getPath(), data.getRawData());
+    }
+
+    public Map<String, List<Long>> getRawData() {
+        return rawData;
+    }
+
+    public String getCreatedUri() {
+        return createdUri;
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
index 13dd28e..9c656a5 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxRsResourceBase.java
@@ -20,10 +20,8 @@ package org.killbill.billing.jaxrs.resources;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
 import java.math.BigDecimal;
 import java.net.URI;
-import java.net.URLDecoder;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -49,7 +47,6 @@ 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.AccountUserApi;
-import org.killbill.billing.invoice.api.Invoice;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentType;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
@@ -59,11 +56,13 @@ import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.Payment;
 import org.killbill.billing.payment.api.PaymentApi;
-import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentOptions;
+import org.killbill.billing.payment.api.PaymentTransaction;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
+import org.killbill.billing.platform.profiling.Profiling;
+import org.killbill.billing.platform.profiling.ProfilingData.ProfilingDataOutput;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
@@ -331,7 +330,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
         return null;
     }
 
-    protected Iterable<PluginProperty> extractPluginProperties(@Nullable final Iterable<String> pluginProperties, PluginProperty...additionalProperties) {
+    protected Iterable<PluginProperty> extractPluginProperties(@Nullable final Iterable<String> pluginProperties, PluginProperty... additionalProperties) {
         final Collection<PluginProperty> properties = new LinkedList<PluginProperty>();
         if (pluginProperties == null) {
             return properties;
@@ -343,7 +342,7 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
             final String value = property.size() == 1 ? null : Joiner.on("=").join(property.subList(1, property.size()));
             properties.add(new PluginProperty(key, value, false));
         }
-        for (PluginProperty  cur : additionalProperties) {
+        for (PluginProperty cur : additionalProperties) {
             properties.add(cur);
         }
         return properties;
@@ -360,16 +359,16 @@ public abstract class JaxRsResourceBase implements JaxrsResource {
 
         final UUID paymentMethodId = externalPayment ? null : account.getPaymentMethodId();
         return paymentApi.createPurchaseWithPaymentControl(account, paymentMethodId, null, amountToPay, account.getCurrency(), paymentExternalKey, transactionExternalKey,
-                                                    properties, createInvoicePaymentControlPluginApiPaymentOptions(externalPayment), callContext);
+                                                           properties, createInvoicePaymentControlPluginApiPaymentOptions(externalPayment), callContext);
     }
 
-
     protected PaymentOptions createInvoicePaymentControlPluginApiPaymentOptions(final boolean isExternalPayment) {
         return new PaymentOptions() {
             @Override
             public boolean isExternalPayment() {
                 return isExternalPayment;
             }
+
             @Override
             public String getPaymentControlPluginName() {
                 /* Contract with plugin */
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/JaxrsUriBuilder.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/JaxrsUriBuilder.java
index cd1842b..7cc3044 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/JaxrsUriBuilder.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/util/JaxrsUriBuilder.java
@@ -23,8 +23,11 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 
+import org.killbill.billing.jaxrs.json.ProfilingDataJson;
 import org.killbill.billing.jaxrs.resources.JaxRsResourceBase;
 import org.killbill.billing.jaxrs.resources.JaxrsResource;
+import org.killbill.billing.platform.profiling.Profiling;
+import org.killbill.billing.platform.profiling.ProfilingData;
 
 public class JaxrsUriBuilder {
 
@@ -36,7 +39,6 @@ public class JaxrsUriBuilder {
                                                 .port(uriInfo.getAbsolutePath().getPort());
 
         final URI location = objectId != null ? uriBuilder.build(objectId) : uriBuilder.build();
-
         return Response.created(location).build();
     }
 
@@ -64,12 +66,15 @@ public class JaxrsUriBuilder {
         tmp.append(UriBuilder.fromResource(theClass).path(theClass, getMethodName).build(objectId).toString());
         final URI newUriFromResource = UriBuilder.fromUri(tmp.toString()).build();
         final Response.ResponseBuilder ri = Response.created(newUriFromResource);
-        return ri.entity(new Object() {
+
+        final ProfilingData profilingData = Profiling.getPerThreadProfilingData();
+        final Object obj = profilingData == null ? new Object() {
             @SuppressWarnings(value = "all")
             public URI getUri() {
-
                 return newUriFromResource;
             }
-        }).build();
+        } : new ProfilingDataJson(profilingData, newUriFromResource);
+
+        return ri.entity(obj).build();
     }
 }

payment/pom.xml 1(+0 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index e0064dc..00e0c82 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -101,7 +101,6 @@
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-platform-base</artifactId>
-            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
index b4f3698..c9231d0 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentGatewayProcessor.java
@@ -37,6 +37,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.GatewayNotification;
 import org.killbill.billing.payment.plugin.api.HostedPaymentPageFormDescriptor;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
@@ -81,18 +82,36 @@ public class PaymentGatewayProcessor extends ProcessorBase {
         this.paymentPluginNotificationDispatcher = new PluginDispatcher<GatewayNotification>(paymentPluginTimeoutSec, executor);
     }
 
+    public GatewayNotification processNotification(final String notification, final String pluginName, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
+        return dispatchWithExceptionHandling(null,
+                                             new Callable<PluginDispatcherReturnType<GatewayNotification>>() {
+                                                 @Override
+                                                 public PluginDispatcherReturnType<GatewayNotification> call() throws PaymentApiException {
+                                                     final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
+                                                     try {
+                                                         final GatewayNotification result = plugin.processNotification(notification, properties, callContext);
+                                                         return PluginDispatcher.createPluginDispatcherReturnType(result);
+                                                     } catch (PaymentPluginApiException e) {
+                                                         throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
+                                                     }
+                                                 }
+                                             }, paymentPluginNotificationDispatcher);
+    }
+
+
+
     public HostedPaymentPageFormDescriptor buildFormDescriptor(final Account account, final Iterable<PluginProperty> customFields, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext internalCallContext) throws PaymentApiException {
         return dispatchWithExceptionHandling(account,
                                              new CallableWithAccountLock<HostedPaymentPageFormDescriptor, PaymentApiException>(locker,
                                                                                                                                account.getExternalKey(),
-                                                                                                                               new WithAccountLockCallback<HostedPaymentPageFormDescriptor, PaymentApiException>() {
-
+                                                                                                                               new WithAccountLockCallback<PluginDispatcherReturnType<HostedPaymentPageFormDescriptor>, PaymentApiException>() {
                                                                                                                                    @Override
-                                                                                                                                   public HostedPaymentPageFormDescriptor doOperation() throws PaymentApiException {
+                                                                                                                                   public PluginDispatcherReturnType<HostedPaymentPageFormDescriptor> doOperation() throws PaymentApiException {
                                                                                                                                        final PaymentPluginApi plugin = getPaymentProviderPlugin(account, internalCallContext);
 
                                                                                                                                        try {
-                                                                                                                                           return plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
+                                                                                                                                           final HostedPaymentPageFormDescriptor result = plugin.buildFormDescriptor(account.getId(), customFields, properties, callContext);
+                                                                                                                                           return PluginDispatcher.createPluginDispatcherReturnType(result);
                                                                                                                                        } catch (final RuntimeException e) {
                                                                                                                                            throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
                                                                                                                                        } catch (final PaymentPluginApiException e) {
@@ -103,22 +122,7 @@ public class PaymentGatewayProcessor extends ProcessorBase {
                                              paymentPluginFormDispatcher);
     }
 
-    public GatewayNotification processNotification(final String notification, final String pluginName, final Iterable<PluginProperty> properties, final CallContext callContext) throws PaymentApiException {
-        return dispatchWithExceptionHandling(null,
-                                      new Callable<GatewayNotification>() {
-                                          @Override
-                                          public GatewayNotification call() throws PaymentApiException {
-                                              final PaymentPluginApi plugin = getPaymentPluginApi(pluginName);
-                                              try {
-                                                  return plugin.processNotification(notification, properties, callContext);
-                                              } catch (PaymentPluginApiException e) {
-                                                  throw new PaymentApiException(ErrorCode.PAYMENT_PLUGIN_EXCEPTION, e.getErrorMessage());
-                                              }
-                                          }
-                                      }, paymentPluginNotificationDispatcher);
-    }
-
-    private static <ReturnType> ReturnType dispatchWithExceptionHandling(@Nullable final Account account, final Callable<ReturnType> callable, PluginDispatcher<ReturnType> pluginFormDispatcher) throws PaymentApiException {
+    private static <ReturnType> ReturnType dispatchWithExceptionHandling(@Nullable final Account account, final Callable<PluginDispatcherReturnType<ReturnType>> callable, PluginDispatcher<ReturnType> pluginFormDispatcher) throws PaymentApiException {
         final UUID accountId = account != null ? account.getId() : null;
         final String accountExternalKey = account != null ? account.getExternalKey() : "";
         try {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
index 69ff4a7..e479bae 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentMethodProcessor.java
@@ -41,6 +41,8 @@ import org.killbill.billing.payment.api.PaymentMethodPlugin;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentMethodModelDao;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.PaymentMethodInfoPlugin;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
@@ -54,9 +56,9 @@ import org.killbill.billing.util.dao.NonEntityDao;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.EntityPaginationBuilder;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
-import org.killbill.bus.api.PersistentBus;
 import org.killbill.clock.Clock;
 import org.killbill.commons.locker.GlobalLocker;
+import org.killbill.commons.locker.LockFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -93,10 +95,10 @@ public class PaymentMethodProcessor extends ProcessorBase {
                                  final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context)
             throws PaymentApiException {
         try {
-            return new WithAccountLock<UUID, PaymentApiException>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<UUID, PaymentApiException>() {
+            final PluginDispatcherReturnType<UUID> result = new WithAccountLock<UUID, PaymentApiException>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<PluginDispatcherReturnType<UUID>, PaymentApiException>() {
 
                 @Override
-                public UUID doOperation() throws PaymentApiException {
+                public PluginDispatcherReturnType<UUID> doOperation() throws PaymentApiException {
                     PaymentMethod pm = null;
                     PaymentPluginApi pluginApi;
                     try {
@@ -116,9 +118,10 @@ public class PaymentMethodProcessor extends ProcessorBase {
                     } catch (final AccountApiException e) {
                         throw new PaymentApiException(e);
                     }
-                    return pm.getId();
+                    return PluginDispatcher.createPluginDispatcherReturnType(pm.getId());
                 }
             });
+            return result.getReturnType();
         } catch (Exception e) {
             throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
         }
@@ -160,7 +163,6 @@ public class PaymentMethodProcessor extends ProcessorBase {
         return buildDefaultPaymentMethod(paymentMethodModel, withPluginInfo, properties, tenantContext, context);
     }
 
-
     private PaymentMethod buildDefaultPaymentMethod(final PaymentMethodModelDao paymentMethodModelDao, final boolean withPluginInfo, final Iterable<PluginProperty> properties, final TenantContext tenantContext, final InternalTenantContext context) throws PaymentApiException {
         final PaymentMethodPlugin paymentMethodPlugin;
         if (withPluginInfo) {
@@ -312,10 +314,10 @@ public class PaymentMethodProcessor extends ProcessorBase {
                                      final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context)
             throws PaymentApiException {
         try {
-            new WithAccountLock<Void, PaymentApiException>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<Void, PaymentApiException>() {
+            new WithAccountLock<Void, PaymentApiException>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<PluginDispatcherReturnType<Void>, PaymentApiException>() {
 
                 @Override
-                public Void doOperation() throws PaymentApiException {
+                public PluginDispatcherReturnType<Void> doOperation() throws PaymentApiException {
                     final PaymentMethodModelDao paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
                     if (paymentMethodModel == null) {
                         throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
@@ -338,7 +340,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                         final PaymentPluginApi pluginApi = getPluginApi(paymentMethodId, context);
                         pluginApi.deletePaymentMethod(account.getId(), paymentMethodId, properties, callContext);
                         paymentDao.deletedPaymentMethod(paymentMethodId, context);
-                        return null;
+                        return PluginDispatcher.createPluginDispatcherReturnType(null);
                     } catch (final PaymentPluginApiException e) {
                         log.warn("Error deleting payment method " + paymentMethodId, e);
                         throw new PaymentApiException(ErrorCode.PAYMENT_DEL_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
@@ -355,10 +357,10 @@ public class PaymentMethodProcessor extends ProcessorBase {
     public void setDefaultPaymentMethod(final Account account, final UUID paymentMethodId, final Iterable<PluginProperty> properties, final CallContext callContext, final InternalCallContext context)
             throws PaymentApiException {
         try {
-            new WithAccountLock<Void, PaymentApiException>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<Void, PaymentApiException>() {
+            new WithAccountLock<Void, PaymentApiException>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<PluginDispatcherReturnType<Void>, PaymentApiException>() {
 
                 @Override
-                public Void doOperation() throws PaymentApiException {
+                public PluginDispatcherReturnType<Void> doOperation() throws PaymentApiException {
                     final PaymentMethodModelDao paymentMethodModel = paymentDao.getPaymentMethod(paymentMethodId, context);
                     if (paymentMethodModel == null) {
                         throw new PaymentApiException(ErrorCode.PAYMENT_NO_SUCH_PAYMENT_METHOD, paymentMethodId);
@@ -369,7 +371,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
 
                         pluginApi.setDefaultPaymentMethod(account.getId(), paymentMethodId, properties, callContext);
                         accountInternalApi.updatePaymentMethod(account.getId(), paymentMethodId, context);
-                        return null;
+                        return PluginDispatcher.createPluginDispatcherReturnType(null);
                     } catch (final PaymentPluginApiException e) {
                         throw new PaymentApiException(ErrorCode.PAYMENT_UPD_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
                     } catch (final AccountApiException e) {
@@ -419,10 +421,9 @@ public class PaymentMethodProcessor extends ProcessorBase {
         }
 
         try {
-            return new WithAccountLock<List<PaymentMethod>, PaymentApiException>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<List<PaymentMethod>, PaymentApiException>() {
-
+            final PluginDispatcherReturnType<List<PaymentMethod>> result = new WithAccountLock<List<PaymentMethod>, PaymentApiException>().processAccountWithLock(locker, account.getExternalKey(), new WithAccountLockCallback<PluginDispatcherReturnType<List<PaymentMethod>>, PaymentApiException>() {
                 @Override
-                public List<PaymentMethod> doOperation() throws PaymentApiException {
+                public PluginDispatcherReturnType<List<PaymentMethod>> doOperation() throws PaymentApiException {
 
                     UUID defaultPaymentMethodId = null;
 
@@ -433,7 +434,7 @@ public class PaymentMethodProcessor extends ProcessorBase {
                         final UUID paymentMethodId = cur.getPaymentMethodId() != null ? cur.getPaymentMethodId() : UUID.randomUUID();
                         // TODO paymentMethod externalKey seems broken here.
                         final PaymentMethod input = new DefaultPaymentMethod(paymentMethodId, paymentMethodId.toString(), account.getId(), pluginName);
-                        final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(input.getId(),  input.getExternalKey(), input.getCreatedDate(), input.getUpdatedDate(),
+                        final PaymentMethodModelDao pmModel = new PaymentMethodModelDao(input.getId(), input.getExternalKey(), input.getCreatedDate(), input.getUpdatedDate(),
                                                                                         input.getAccountId(), input.getPluginName(), input.isActive());
                         finalPaymentMethods.add(pmModel);
 
@@ -451,27 +452,28 @@ public class PaymentMethodProcessor extends ProcessorBase {
                                                                                                                  pluginName,
                                                                                                                  finalPaymentMethods,
                                                                                                                  context);
+
                     try {
                         pluginApi.resetPaymentMethods(account.getId(), pluginPmsWithId, properties, callContext);
                     } catch (final PaymentPluginApiException e) {
                         log.warn("Error resetting payment methods for account " + account.getId() + " and plugin " + pluginName, e);
                         throw new PaymentApiException(ErrorCode.PAYMENT_REFRESH_PAYMENT_METHOD, account.getId(), e.getErrorMessage());
                     }
-
                     try {
                         updateDefaultPaymentMethodIfNeeded(pluginName, account, defaultPaymentMethodId, context);
                     } catch (final AccountApiException e) {
                         throw new PaymentApiException(e);
                     }
-
-                    return ImmutableList.<PaymentMethod>copyOf(Collections2.transform(refreshedPaymentMethods, new Function<PaymentMethodModelDao, PaymentMethod>() {
+                    final List<PaymentMethod> result = ImmutableList.<PaymentMethod>copyOf(Collections2.transform(refreshedPaymentMethods, new Function<PaymentMethodModelDao, PaymentMethod>() {
                         @Override
                         public PaymentMethod apply(final PaymentMethodModelDao input) {
                             return new DefaultPaymentMethod(input, null);
                         }
                     }));
+                    return PluginDispatcher.createPluginDispatcherReturnType(result);
                 }
             });
+            return result.getReturnType();
         } catch (Exception e) {
             throw new PaymentApiException(e, ErrorCode.PAYMENT_INTERNAL_ERROR, Objects.firstNonNull(e.getMessage(), ""));
         }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
index 0a8db62..02295f3 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/ProcessorBase.java
@@ -39,6 +39,7 @@ import org.killbill.billing.payment.api.TransactionStatus;
 import org.killbill.billing.payment.dao.PaymentDao;
 import org.killbill.billing.payment.dao.PaymentMethodModelDao;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.tag.TagInternalApi;
 import org.killbill.billing.util.api.TagApiException;
@@ -175,33 +176,34 @@ public abstract class ProcessorBase {
 
 
     // TODO Rename - there is no lock!
-    public interface WithAccountLockCallback<ReturnType, ExceptionType extends Exception> {
-        public ReturnType doOperation() throws ExceptionType;
+    public interface WithAccountLockCallback<PluginDispatcherReturnType, ExceptionType extends Exception> {
+        public PluginDispatcherReturnType doOperation() throws ExceptionType;
     }
 
-    public static class CallableWithAccountLock<ReturnType, ExceptionType extends Exception> implements Callable<ReturnType> {
+    public static class CallableWithAccountLock<ReturnType, ExceptionType extends Exception> implements Callable<PluginDispatcherReturnType<ReturnType>> {
 
         private final GlobalLocker locker;
         private final String accountExternalKey;
-        private final WithAccountLockCallback<ReturnType, ExceptionType> callback;
+        private final WithAccountLockCallback<PluginDispatcherReturnType<ReturnType>, ExceptionType> callback;
+
 
         public CallableWithAccountLock(final GlobalLocker locker,
                                        final String accountExternalKey,
-                                       final WithAccountLockCallback<ReturnType, ExceptionType> callback) {
+                                       final WithAccountLockCallback<PluginDispatcherReturnType<ReturnType>, ExceptionType> callback) {
             this.locker = locker;
             this.accountExternalKey = accountExternalKey;
             this.callback = callback;
         }
 
         @Override
-        public ReturnType call() throws ExceptionType, LockFailedException {
+        public PluginDispatcherReturnType<ReturnType> call() throws ExceptionType, LockFailedException {
             return new WithAccountLock<ReturnType, ExceptionType>().processAccountWithLock(locker, accountExternalKey, callback);
         }
     }
 
-    public static class WithAccountLock<T, ExceptionType extends Exception> {
+    public static class WithAccountLock<ReturnType, ExceptionType extends Exception> {
 
-        public T processAccountWithLock(final GlobalLocker locker, final String accountExternalKey, final WithAccountLockCallback<T,ExceptionType > callback)
+        public PluginDispatcherReturnType<ReturnType> processAccountWithLock(final GlobalLocker locker, final String accountExternalKey, final WithAccountLockCallback<PluginDispatcherReturnType<ReturnType>, ExceptionType > callback)
                 throws ExceptionType, LockFailedException {
             GlobalLock lock = null;
             try {
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
index 68cf540..d4da958 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/OperationCallbackBase.java
@@ -27,6 +27,7 @@ import org.killbill.billing.account.api.Account;
 import org.killbill.billing.payment.core.ProcessorBase.CallableWithAccountLock;
 import org.killbill.billing.payment.core.ProcessorBase.WithAccountLockCallback;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.commons.locker.GlobalLocker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -53,12 +54,12 @@ public abstract class OperationCallbackBase {
     // The dispatcher may throw a TimeoutException, ExecutionException, or InterruptedException; those will be handled in specific
     // callback to eventually throw a OperationException, that will be used to drive the state machine in the right direction.
     //
-    protected <ExceptionType extends Exception> OperationResult dispatchWithAccountLockAndTimeout(final WithAccountLockCallback<OperationResult, ExceptionType> callback) throws OperationException {
+    protected <ExceptionType extends Exception> OperationResult dispatchWithAccountLockAndTimeout(final WithAccountLockCallback<PluginDispatcherReturnType<OperationResult>, ExceptionType> callback) throws OperationException {
         final Account account = paymentStateContext.getAccount();
         logger.debug("Dispatching plugin call for account {}", account.getExternalKey());
 
         try {
-            final Callable<OperationResult> task = new CallableWithAccountLock<OperationResult, ExceptionType>(locker,
+            final Callable<PluginDispatcherReturnType<OperationResult>> task = new CallableWithAccountLock<OperationResult, ExceptionType>(locker,
                                                                                                                account.getExternalKey(),
                                                                                                                callback);
             final OperationResult operationResult = paymentPluginDispatcher.dispatchWithTimeout(task);
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
index 92a5343..556fb42 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/PaymentOperation.java
@@ -33,6 +33,7 @@ import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.ProcessorBase.WithAccountLockCallback;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentPluginStatus;
@@ -133,10 +134,11 @@ public abstract class PaymentOperation extends OperationCallbackBase implements 
     }
 
     private OperationResult doOperationCallbackWithDispatchAndAccountLock() throws OperationException {
-        return dispatchWithAccountLockAndTimeout(new WithAccountLockCallback<OperationResult, OperationException>() {
+        return dispatchWithAccountLockAndTimeout(new WithAccountLockCallback<PluginDispatcherReturnType<OperationResult>, OperationException>() {
             @Override
-            public OperationResult doOperation() throws OperationException {
-                return doSimpleOperationCallback();
+            public PluginDispatcherReturnType<OperationResult> doOperation() throws OperationException {
+                final OperationResult result = doSimpleOperationCallback();
+                return PluginDispatcher.createPluginDispatcherReturnType(result);
             }
         });
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java
index 7d102d6..91e1f92 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryCompletionOperationCallback.java
@@ -26,6 +26,7 @@ import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.ProcessorBase.WithAccountLockCallback;
 import org.killbill.billing.payment.dao.PaymentTransactionModelDao;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.retry.plugin.api.PaymentControlContext;
 import org.killbill.billing.retry.plugin.api.PaymentControlPluginApi;
 import org.killbill.commons.locker.GlobalLocker;
@@ -39,9 +40,9 @@ public class RetryCompletionOperationCallback extends RetryOperationCallback {
     @Override
     public OperationResult doOperationCallback() throws OperationException {
 
-        return dispatchWithAccountLockAndTimeout(new WithAccountLockCallback<OperationResult, OperationException>() {
+        return dispatchWithAccountLockAndTimeout(new WithAccountLockCallback<PluginDispatcherReturnType<OperationResult>, OperationException>() {
             @Override
-            public OperationResult doOperation() throws OperationException {
+            public PluginDispatcherReturnType<OperationResult> doOperation() throws OperationException {
                 final PaymentTransactionModelDao transaction = paymentStateContext.getPaymentTransactionModelDao();
                 final PaymentControlContext updatedPaymentControlContext = new DefaultPaymentControlContext(paymentStateContext.getAccount(),
                                                                                                             paymentStateContext.getPaymentMethodId(),
@@ -60,7 +61,7 @@ public class RetryCompletionOperationCallback extends RetryOperationCallback {
                                                                                                             paymentStateContext.callContext);
 
                 onCompletion(retryablePaymentStateContext.getPluginName(), updatedPaymentControlContext);
-                return OperationResult.SUCCESS;
+                return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.SUCCESS);
             }
         });
     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
index 3718fd5..d73d022 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/sm/RetryOperationCallback.java
@@ -40,6 +40,7 @@ import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.PaymentProcessor;
 import org.killbill.billing.payment.core.ProcessorBase.WithAccountLockCallback;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.retry.plugin.api.FailureCallResult;
 import org.killbill.billing.retry.plugin.api.PaymentControlApiException;
 import org.killbill.billing.retry.plugin.api.PaymentControlContext;
@@ -75,10 +76,10 @@ public abstract class RetryOperationCallback extends OperationCallbackBase imple
     @Override
     public OperationResult doOperationCallback() throws OperationException {
 
-        return dispatchWithAccountLockAndTimeout(new WithAccountLockCallback<OperationResult, OperationException>() {
+        return dispatchWithAccountLockAndTimeout(new WithAccountLockCallback<PluginDispatcherReturnType<OperationResult>, OperationException>() {
 
             @Override
-            public OperationResult doOperation() throws OperationException {
+            public PluginDispatcherReturnType<OperationResult> doOperation() throws OperationException {
 
                 final PaymentControlContext paymentControlContext = new DefaultPaymentControlContext(paymentStateContext.getAccount(),
                                                                                                      paymentStateContext.getPaymentMethodId(),
@@ -98,7 +99,7 @@ public abstract class RetryOperationCallback extends OperationCallbackBase imple
                     pluginResult = getPluginResult(retryablePaymentStateContext.getPluginName(), paymentControlContext);
                     if (pluginResult.isAborted()) {
                         // Transition to ABORTED
-                        return OperationResult.EXCEPTION;
+                        return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.EXCEPTION);
                     }
                 } catch (PaymentControlApiException e) {
                     // Transition to ABORTED and throw PaymentControlApiException to caller.
@@ -136,7 +137,7 @@ public abstract class RetryOperationCallback extends OperationCallbackBase imple
                                                                                                                     paymentStateContext.callContext);
 
                         onCompletion(retryablePaymentStateContext.getPluginName(), updatedPaymentControlContext);
-                        return OperationResult.SUCCESS;
+                        return PluginDispatcher.createPluginDispatcherReturnType(OperationResult.SUCCESS);
                     } else {
                         throw new OperationException(null, getOperationResultAndSetContext(retryablePaymentStateContext, paymentControlContext));
                     }
diff --git a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
index 519107e..dd1ba05 100644
--- a/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
+++ b/payment/src/main/java/org/killbill/billing/payment/dispatcher/PluginDispatcher.java
@@ -25,6 +25,11 @@ import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import org.killbill.billing.platform.profiling.Profiling;
+import org.killbill.billing.platform.profiling.ProfilingData;
+
+import com.google.common.base.Preconditions;
+
 public class PluginDispatcher<ReturnType> {
 
     private final TimeUnit DEEFAULT_PLUGIN_TIMEOUT_UNIT = TimeUnit.SECONDS;
@@ -38,13 +43,56 @@ public class PluginDispatcher<ReturnType> {
     }
 
     // TODO Once we switch fully to automata, should this throw PaymentPluginApiException instead?
-    public ReturnType dispatchWithTimeout(final Callable<ReturnType> task) throws TimeoutException, ExecutionException, InterruptedException {
+    public ReturnType dispatchWithTimeout(final Callable<PluginDispatcherReturnType<ReturnType>> task) throws TimeoutException, ExecutionException, InterruptedException {
         return dispatchWithTimeout(task, timeoutSeconds, DEEFAULT_PLUGIN_TIMEOUT_UNIT);
     }
 
-    public ReturnType dispatchWithTimeout(final Callable<ReturnType> task, final long timeout, final TimeUnit unit)
+    public ReturnType dispatchWithTimeout(final Callable<PluginDispatcherReturnType<ReturnType>> task, final long timeout, final TimeUnit unit)
             throws TimeoutException, ExecutionException, InterruptedException {
-        final Future<ReturnType> future = executor.submit(task);
-        return future.get(timeout, unit);
+
+        final Future<PluginDispatcherReturnType<ReturnType>> future = executor.submit(task);
+        final PluginDispatcherReturnType<ReturnType> pluginDispatcherResult = future.get(timeout, unit);
+
+        if (pluginDispatcherResult instanceof WithProfilingPluginDispatcherReturnType) {
+            // Transfer state from dispatch thread into current one.
+            final ProfilingData currentThreadProfilingData = Profiling.getPerThreadProfilingData();
+            if (currentThreadProfilingData != null) {
+                currentThreadProfilingData.merge(((WithProfilingPluginDispatcherReturnType)pluginDispatcherResult).getProfilingData());
+            }
+        }
+        return pluginDispatcherResult.getReturnType();
+    }
+
+    public interface PluginDispatcherReturnType<ReturnType> {
+        public ReturnType getReturnType();
+    }
+
+    public interface WithProfilingPluginDispatcherReturnType<ReturnType> extends PluginDispatcherReturnType<ReturnType> {
+        public ProfilingData getProfilingData();
     }
+
+    public static class DefaultWithProfilingPluginDispatcherReturnType<ReturnType> implements WithProfilingPluginDispatcherReturnType<ReturnType> {
+        private final ReturnType returnType;
+        private final ProfilingData profilingData;
+
+        public DefaultWithProfilingPluginDispatcherReturnType(final ReturnType returnType, final ProfilingData profilingData) {
+            this.returnType = returnType;
+            this.profilingData = profilingData;
+        }
+
+        @Override
+        public ReturnType getReturnType() {
+            return returnType;
+        }
+
+        @Override
+        public ProfilingData getProfilingData() {
+            return profilingData;
+        }
+    }
+
+    public static <ReturnType> PluginDispatcherReturnType<ReturnType> createPluginDispatcherReturnType(final ReturnType returnType) {
+        return new DefaultWithProfilingPluginDispatcherReturnType(returnType, Profiling.getPerThreadProfilingData());
+    }
+
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
index 744a571..c7cf91f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
+++ b/payment/src/main/java/org/killbill/billing/payment/glue/PaymentModule.java
@@ -20,8 +20,11 @@ package org.killbill.billing.payment.glue;
 
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
 import javax.inject.Provider;
 
@@ -52,6 +55,7 @@ import org.killbill.billing.payment.retry.DefaultRetryService;
 import org.killbill.billing.payment.retry.DefaultRetryService.DefaultRetryServiceScheduler;
 import org.killbill.billing.payment.retry.RetryService;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.platform.profiling.WithProfilingThreadPoolExecutor;
 import org.killbill.billing.retry.plugin.api.PaymentControlPluginApi;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.billing.util.glue.KillBillModule;
@@ -136,6 +140,22 @@ public class PaymentModule extends KillBillModule {
     }
 
     protected void installProcessors(final PaymentConfig paymentConfig) {
+
+
+        final ExecutorService pluginExecutorService = new WithProfilingThreadPoolExecutor(paymentConfig.getPaymentPluginThreadNb(), paymentConfig.getPaymentPluginThreadNb(),
+                                      0L, TimeUnit.MILLISECONDS,
+                                      new LinkedBlockingQueue<Runnable>(),
+                                      new ThreadFactory() {
+
+                                          @Override
+                                          public Thread newThread(final Runnable r) {
+                                              final Thread th = new Thread(r);
+                                              th.setName(PLUGIN_THREAD_PREFIX + th.getId());
+                                              return th;
+                                          }
+                                      });
+
+        /*
         final ExecutorService pluginExecutorService = Executors.newFixedThreadPool(paymentConfig.getPaymentPluginThreadNb(), new ThreadFactory() {
 
             @Override
@@ -145,6 +165,9 @@ public class PaymentModule extends KillBillModule {
                 return th;
             }
         });
+        */
+
+
         bind(ExecutorService.class).annotatedWith(Names.named(PLUGIN_EXECUTOR_NAMED)).toInstance(pluginExecutorService);
         bind(PaymentProcessor.class).asEagerSingleton();
         bind(PluginControlledPaymentProcessor.class).asEagerSingleton();
diff --git a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
index 007c1f0..0c9bb88 100644
--- a/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
+++ b/payment/src/test/java/org/killbill/billing/payment/core/sm/TestPluginOperation.java
@@ -38,11 +38,16 @@ import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.payment.api.TransactionType;
 import org.killbill.billing.payment.core.ProcessorBase.WithAccountLockCallback;
 import org.killbill.billing.payment.dispatcher.PluginDispatcher;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApiException;
 import org.killbill.billing.payment.plugin.api.PaymentTransactionInfoPlugin;
+import org.killbill.billing.platform.profiling.Profiling;
+import org.killbill.billing.platform.profiling.ProfilingData.ProfilingDataOutput;
 import org.killbill.commons.locker.GlobalLocker;
 import org.killbill.commons.locker.memory.MemoryGlobalLocker;
 import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -55,8 +60,11 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
     private final GlobalLocker locker = new MemoryGlobalLocker();
     private final Account account = Mockito.mock(Account.class);
 
+    private static final Logger logger = LoggerFactory.getLogger(TestPluginOperation.class);
+
     @BeforeMethod(groups = "fast")
-    public void setUp() throws Exception {
+    public void beforeMethod() throws Exception {
+        super.beforeMethod();
         Mockito.when(account.getExternalKey()).thenReturn(UUID.randomUUID().toString());
     }
 
@@ -198,7 +206,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
         return new PluginOperationTest(daoHelper, locker, paymentPluginDispatcher, paymentStateContext);
     }
 
-    private static final class CallbackTest implements WithAccountLockCallback<OperationResult, PaymentApiException> {
+    private static final class CallbackTest implements WithAccountLockCallback<PluginDispatcherReturnType<OperationResult>, PaymentApiException> {
 
         private final AtomicInteger runCount = new AtomicInteger(0);
 
@@ -232,7 +240,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
         }
 
         @Override
-        public OperationResult doOperation() throws PaymentApiException {
+        public PluginDispatcherReturnType<OperationResult> doOperation() throws PaymentApiException {
             try {
                 if (available != null) {
                     available.acquire();
@@ -257,8 +265,7 @@ public class TestPluginOperation extends PaymentTestSuiteNoDB {
                     available.release();
                 }
             }
-
-            return null;
+            return PluginDispatcher.createPluginDispatcherReturnType(null);
         }
 
         public int getRunCount() {
diff --git a/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java b/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
index e3fc071..d226d73 100644
--- a/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
+++ b/payment/src/test/java/org/killbill/billing/payment/dispatcher/TestPluginDispatcher.java
@@ -22,10 +22,10 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import org.killbill.automaton.OperationException;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.payment.PaymentTestSuiteNoDB;
 import org.killbill.billing.payment.api.PaymentApiException;
+import org.killbill.billing.payment.dispatcher.PluginDispatcher.PluginDispatcherReturnType;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -37,9 +37,9 @@ public class TestPluginDispatcher extends PaymentTestSuiteNoDB {
     public void testDispatchWithTimeout() throws TimeoutException, PaymentApiException {
         boolean gotIt = false;
         try {
-            voidPluginDispatcher.dispatchWithTimeout(new Callable<Void>() {
+            voidPluginDispatcher.dispatchWithTimeout(new Callable<PluginDispatcherReturnType<Void>>() {
                 @Override
-                public Void call() throws Exception {
+                public PluginDispatcherReturnType<Void> call() throws Exception {
                     Thread.sleep(1000);
                     return null;
                 }
@@ -59,9 +59,9 @@ public class TestPluginDispatcher extends PaymentTestSuiteNoDB {
     public void testDispatchWithPaymentApiException() throws TimeoutException, PaymentApiException {
         boolean gotIt = false;
         try {
-            voidPluginDispatcher.dispatchWithTimeout(new Callable<Void>() {
+            voidPluginDispatcher.dispatchWithTimeout(new Callable<PluginDispatcherReturnType<Void>>() {
                 @Override
-                public Void call() throws Exception {
+                public PluginDispatcherReturnType<Void> call() throws Exception {
                     throw new PaymentApiException(ErrorCode.PAYMENT_ADD_PAYMENT_METHOD, "foo", "foo");
                 }
             }, 100, TimeUnit.MILLISECONDS);
@@ -84,9 +84,9 @@ public class TestPluginDispatcher extends PaymentTestSuiteNoDB {
     public void testDispatchWithRuntimeException() throws TimeoutException, PaymentApiException {
         boolean gotIt = false;
         try {
-            voidPluginDispatcher.dispatchWithTimeout(new Callable<Void>() {
+            voidPluginDispatcher.dispatchWithTimeout(new Callable<PluginDispatcherReturnType<Void>>() {
                 @Override
-                public Void call() throws Exception {
+                public PluginDispatcherReturnType<Void> call() throws Exception {
                     throw new RuntimeException("whatever");
                 }
             }, 100, TimeUnit.MILLISECONDS);
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
index f80693e..4df4fbd 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteNoDB.java
@@ -34,6 +34,7 @@ import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.payment.retry.DefaultRetryService;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.platform.profiling.Profiling;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.testng.annotations.AfterMethod;
@@ -93,6 +94,7 @@ public abstract class PaymentTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @BeforeMethod(groups = "fast")
     public void beforeMethod() throws Exception {
         eventBus.start();
+        Profiling.resetPerThreadProfilingData();
     }
 
     @AfterMethod(groups = "fast")
diff --git a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
index 0f78ff0..05b20bd 100644
--- a/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
+++ b/payment/src/test/java/org/killbill/billing/payment/PaymentTestSuiteWithEmbeddedDB.java
@@ -31,6 +31,7 @@ import org.killbill.billing.payment.glue.TestPaymentModuleWithEmbeddedDB;
 import org.killbill.billing.payment.plugin.api.PaymentPluginApi;
 import org.killbill.billing.payment.provider.MockPaymentProviderPlugin;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.platform.profiling.Profiling;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.bus.api.PersistentBus;
 import org.testng.annotations.AfterMethod;
@@ -84,6 +85,8 @@ public abstract class PaymentTestSuiteWithEmbeddedDB extends GuicyKillbillTestSu
     public void beforeMethod() throws Exception {
         super.beforeMethod();
         eventBus.start();
+        Profiling.resetPerThreadProfilingData();
+
     }
 
     @AfterMethod(groups = "slow")

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index ab02c64..58c601b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.7.18</version>
+        <version>0.7.19-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.11.8-SNAPSHOT</version>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java b/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
index 5304e7a..1211004 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/listeners/KillbillGuiceListener.java
@@ -25,6 +25,7 @@ import org.killbill.billing.jaxrs.util.KillbillEventHandler;
 import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.platform.config.DefaultKillbillConfigSource;
 import org.killbill.billing.server.modules.KillbillServerModule;
+import org.killbill.billing.server.profiling.ProfilingFilter;
 import org.killbill.billing.server.security.TenantFilter;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.commons.skeleton.modules.BaseServerModuleBuilder;
@@ -53,9 +54,11 @@ public class KillbillGuiceListener extends KillbillPlatformGuiceListener {
             builder.addFilter("/*", TenantFilter.class);
         }
 
+        builder.addFilter("/*", ProfilingFilter.class);
         return builder.build();
     }
 
+
     @Override
     protected Module getModule(final ServletContext servletContext) {
         return new KillbillServerModule(servletContext, config, configSource);
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
index 8d19409..8ff2d63 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillServerModule.java
@@ -66,6 +66,7 @@ import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.ExportModule;
 import org.killbill.billing.util.glue.GlobalLockerModule;
 import org.killbill.billing.util.glue.KillBillShiroAopModule;
+import org.killbill.billing.util.glue.KillbillApiAopModule;
 import org.killbill.billing.util.glue.NonEntityDaoModule;
 import org.killbill.billing.util.glue.RecordIdModule;
 import org.killbill.billing.util.glue.SecurityModule;
@@ -135,6 +136,7 @@ public class KillbillServerModule extends KillbillPlatformModule {
         install(new ExportModule(configSource));
         install(new GlobalLockerModule(embeddedDB.getDBEngine(), configSource));
         install(new KillBillShiroAopModule());
+        install(new KillbillApiAopModule());
         install(new KillBillShiroWebModule(servletContext, skifeConfigSource));
         install(new NonEntityDaoModule(configSource));
         install(new PaymentModule(configSource));
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/profiling/ProfilingFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/profiling/ProfilingFilter.java
new file mode 100644
index 0000000..e6bc5df
--- /dev/null
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/profiling/ProfilingFilter.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.server.profiling;
+
+import java.io.IOException;
+
+import javax.inject.Singleton;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+import org.killbill.billing.platform.profiling.Profiling;
+import org.killbill.billing.platform.profiling.ProfilingData.ProfilingDataOutput;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class ProfilingFilter implements Filter {
+
+    private final static Logger logger = LoggerFactory.getLogger(ProfilingFilter.class);
+
+    private final static String PROFILING_HEADER_REQ = "X-Killbill-Profiling-Req";
+    private final static String PROFILING_HEADER_RESP = "X-Killbill-Profiling-Resp";
+
+    @Override
+    public void init(final FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
+
+        final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
+        final String profilingHeaderRequest = httpServletRequest.getHeader(PROFILING_HEADER_REQ);
+        if (profilingHeaderRequest != null) {
+            try {
+                final ProfilingDataOutput profilingOutput = ProfilingDataOutput.valueOf(profilingHeaderRequest);
+                Profiling.setPerThreadProfilingData(profilingOutput);
+            } catch (IllegalArgumentException e) {
+                logger.info("Profiling data output " + profilingHeaderRequest + " is not supported, profiling NOT enabled");
+            }
+        }
+        try {
+            filterChain.doFilter(servletRequest, servletResponse);
+        } finally {
+            Profiling.resetPerThreadProfilingData();
+        }
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+}
diff --git a/profiles/killbill/src/main/resources/killbill-server.properties b/profiles/killbill/src/main/resources/killbill-server.properties
index 1c6344d..2eeabec 100644
--- a/profiles/killbill/src/main/resources/killbill-server.properties
+++ b/profiles/killbill/src/main/resources/killbill-server.properties
@@ -20,6 +20,7 @@
 org.killbill.dao.url=jdbc:mysql://127.0.0.1:3306/killbill
 org.killbill.dao.user=root
 org.killbill.dao.password=root
+org.killbill.dao.logLevel=DEBUG
 org.killbill.billing.osgi.dao.url=jdbc:mysql://127.0.0.1:3306/killbill
 org.killbill.billing.osgi.dao.user=root
 org.killbill.billing.osgi.dao.password=root
diff --git a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
index 205e680..fed4c77 100644
--- a/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
+++ b/profiles/killpay/src/main/java/org/killbill/billing/server/modules/KillpayServerModule.java
@@ -52,6 +52,7 @@ import org.killbill.billing.util.glue.CustomFieldModule;
 import org.killbill.billing.util.glue.ExportModule;
 import org.killbill.billing.util.glue.GlobalLockerModule;
 import org.killbill.billing.util.glue.KillBillShiroAopModule;
+import org.killbill.billing.util.glue.KillbillApiAopModule;
 import org.killbill.billing.util.glue.NonEntityDaoModule;
 import org.killbill.billing.util.glue.RecordIdModule;
 import org.killbill.billing.util.glue.SecurityModule;
@@ -75,6 +76,7 @@ public class KillpayServerModule extends KillbillServerModule {
         install(new ExportModule(configSource));
         install(new GlobalLockerModule(embeddedDB.getDBEngine(), configSource));
         install(new KillBillShiroAopModule());
+        install(new KillbillApiAopModule());
         install(new KillBillShiroWebModule(servletContext, skifeConfigSource));
         install(new NonEntityDaoModule(configSource));
         install(new PaymentModule(configSource));

util/pom.xml 4(+4 -0)

diff --git a/util/pom.xml b/util/pom.xml
index 3dbf8f7..a69c56d 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -134,6 +134,10 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.billing</groupId>
+            <artifactId>killbill-platform-base</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.billing</groupId>
             <artifactId>killbill-platform-osgi-api</artifactId>
             <scope>test</scope>
         </dependency>
diff --git a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
index 456bf95..2651f2c 100644
--- a/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
+++ b/util/src/main/java/org/killbill/billing/util/entity/dao/EntitySqlDaoWrapperInvocationHandler.java
@@ -30,6 +30,8 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.killbill.billing.platform.profiling.Profiling;
+import org.killbill.billing.platform.profiling.Profiling.WithProfilingCallback;
 import org.killbill.billing.util.tag.dao.UUIDCollectionBinder;
 import org.skife.jdbi.v2.Binding;
 import org.skife.jdbi.v2.StatementContext;
@@ -83,6 +85,7 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
     private final CacheControllerDispatcher cacheControllerDispatcher;
     private final Clock clock;
     private final NonEntityDao nonEntityDao;
+    private final Profiling prof;
 
     public EntitySqlDaoWrapperInvocationHandler(final Class<S> sqlDaoClass, final S sqlDao, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher, final NonEntityDao nonEntityDao) {
         this.sqlDaoClass = sqlDaoClass;
@@ -90,12 +93,18 @@ public class EntitySqlDaoWrapperInvocationHandler<S extends EntitySqlDao<M, E>, 
         this.clock = clock;
         this.cacheControllerDispatcher = cacheControllerDispatcher;
         this.nonEntityDao = nonEntityDao;
+        this.prof = new Profiling<Object>();
     }
 
     @Override
     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
         try {
-            return invokeSafely(proxy, method, args);
+            return prof.executeWithProfiling("DAO:" + sqlDaoClass.getSimpleName() + ":" + method.getName(), new WithProfilingCallback() {
+                @Override
+                public Object execute() throws Throwable {
+                    return invokeSafely(proxy, method, args);
+                }
+            });
         } catch (Throwable t) {
             if (t.getCause() != null && t.getCause().getCause() != null && DBIException.class.isAssignableFrom(t.getCause().getClass())) {
                 // Likely a JDBC error, try to extract the SQL statement and JDBI bindings
diff --git a/util/src/main/java/org/killbill/billing/util/glue/KillbillApiAopModule.java b/util/src/main/java/org/killbill/billing/util/glue/KillbillApiAopModule.java
new file mode 100644
index 0000000..ce34cc8
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/glue/KillbillApiAopModule.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014 Groupon, Inc
+ * Copyright 2014 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.util.glue;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.killbill.billing.KillbillApi;
+import org.killbill.billing.platform.profiling.Profiling;
+import org.killbill.billing.platform.profiling.Profiling.WithProfilingCallback;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.matcher.Matchers;
+
+public class KillbillApiAopModule extends AbstractModule {
+
+    @Override
+    protected void configure() {
+
+        bindInterceptor(Matchers.subclassesOf(KillbillApi.class),
+                        Matchers.any(),
+                        new ProfilingMethodInterceptor());
+    }
+
+    public static class ProfilingMethodInterceptor implements MethodInterceptor {
+
+        private final Profiling prof = new Profiling<Object>();
+
+        @Override
+        public Object invoke(final MethodInvocation invocation) throws Throwable {
+            return prof.executeWithProfiling("API:" + invocation.getMethod().getName(), new WithProfilingCallback() {
+                @Override
+                public Object execute() throws Throwable {
+                    return invocation.proceed();
+                }
+            });
+        }
+    }
+}