killbill-memoizeit

jaxrs: Add new threadpool to allow some apis to run independant

9/24/2015 12:45:40 AM

Details

diff --git a/api/src/main/java/org/killbill/billing/jaxrs/JaxrsService.java b/api/src/main/java/org/killbill/billing/jaxrs/JaxrsService.java
new file mode 100644
index 0000000..6d4ae55
--- /dev/null
+++ b/api/src/main/java/org/killbill/billing/jaxrs/JaxrsService.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs;
+
+import org.killbill.billing.platform.api.KillbillService;
+
+public interface JaxrsService extends KillbillService {
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/DefaultJaxrsService.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/DefaultJaxrsService.java
new file mode 100644
index 0000000..bbd4d7b
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/DefaultJaxrsService.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.platform.api.LifecycleHandlerType;
+import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
+import org.killbill.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import org.killbill.notificationq.api.NotificationQueueService.NotificationQueueAlreadyExists;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultJaxrsService implements JaxrsService {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultJaxrsService.class);
+
+    private static final String JAXRS_SERVICE_NAME = "jaxrs-service";
+
+    private final JaxrsExecutors jaxrsExecutors;
+
+    @Inject
+    public DefaultJaxrsService(final JaxrsExecutors jaxrsExecutors) {
+        this.jaxrsExecutors = jaxrsExecutors;
+    }
+
+    @Override
+    public String getName() {
+        return JAXRS_SERVICE_NAME;
+    }
+
+    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
+    public void initialize() throws NotificationQueueAlreadyExists {
+        jaxrsExecutors.initialize();
+    }
+
+    @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
+    public void stop() throws NoSuchNotificationQueue {
+        try {
+            jaxrsExecutors.stop();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            log.error("JaxrsService got interrupted", e);
+        }
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/glue/DefaultJaxrsModule.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/glue/DefaultJaxrsModule.java
new file mode 100644
index 0000000..f19b31d
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/glue/DefaultJaxrsModule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.glue;
+
+import org.killbill.billing.jaxrs.DefaultJaxrsService;
+import org.killbill.billing.jaxrs.JaxrsExecutors;
+import org.killbill.billing.jaxrs.JaxrsService;
+import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.util.config.JaxrsConfig;
+import org.killbill.billing.util.glue.KillBillModule;
+import org.skife.config.ConfigurationObjectFactory;
+
+public class DefaultJaxrsModule extends KillBillModule {
+
+    public DefaultJaxrsModule(final KillbillConfigSource configSource) {
+        super(configSource);
+    }
+
+    @Override
+    protected void configure() {
+        final ConfigurationObjectFactory factory = new ConfigurationObjectFactory(skifeConfigSource);
+        final JaxrsConfig jaxrsConfig = factory.build(JaxrsConfig.class);
+        bind(JaxrsConfig.class).toInstance(jaxrsConfig);
+
+        bind(JaxrsExecutors.class).asEagerSingleton();
+        bind(JaxrsService.class).to(DefaultJaxrsService.class).asEagerSingleton();
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/JaxrsExecutors.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/JaxrsExecutors.java
new file mode 100644
index 0000000..b2625f3
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/JaxrsExecutors.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.util.config.JaxrsConfig;
+import org.killbill.commons.concurrent.WithProfilingThreadPoolExecutor;
+
+public class JaxrsExecutors {
+
+
+    private static final long TIMEOUT_EXECUTOR_SEC = 3L;
+
+    private static final String JAXRS_THREAD_PREFIX = "jaxrs-th-";
+    private static final String JAXRS_TH_GROUP_NAME = "jaxrs-grp";
+
+
+    private final JaxrsConfig JaxrsConfig;
+
+    private volatile ExecutorService jaxrsExecutorService;
+
+    @Inject
+    public JaxrsExecutors(JaxrsConfig JaxrsConfig) {
+        this.JaxrsConfig = JaxrsConfig;
+
+    }
+
+    public void initialize() {
+        this.jaxrsExecutorService = createJaxrsExecutorService();
+    }
+
+
+    public void stop() throws InterruptedException {
+        jaxrsExecutorService.shutdownNow();
+        jaxrsExecutorService.awaitTermination(TIMEOUT_EXECUTOR_SEC, TimeUnit.SECONDS);
+        jaxrsExecutorService = null;
+
+    }
+
+    public ExecutorService getJaxrsExecutorService() {
+        return jaxrsExecutorService;
+    }
+
+    private ExecutorService createJaxrsExecutorService() {
+        return new WithProfilingThreadPoolExecutor(JaxrsConfig.getJaxrsThreadNb(),
+                                                   JaxrsConfig.getJaxrsThreadNb(),
+                                                   0L,
+                                                   TimeUnit.MILLISECONDS,
+                                                   new LinkedBlockingQueue<Runnable>(),
+                                                   new ThreadFactory() {
+
+                                                       @Override
+                                                       public Thread newThread(final Runnable r) {
+                                                           final Thread th = new Thread(new ThreadGroup(JAXRS_TH_GROUP_NAME), r);
+                                                           th.setName(JAXRS_THREAD_PREFIX + th.getId());
+                                                           return th;
+                                                       }
+                                                   });
+
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 374fb62..66af04c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -26,6 +26,12 @@ import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
@@ -60,6 +66,7 @@ import org.killbill.billing.invoice.api.InvoiceApiException;
 import org.killbill.billing.invoice.api.InvoicePayment;
 import org.killbill.billing.invoice.api.InvoicePaymentApi;
 import org.killbill.billing.invoice.api.InvoiceUserApi;
+import org.killbill.billing.jaxrs.JaxrsExecutors;
 import org.killbill.billing.jaxrs.json.AccountEmailJson;
 import org.killbill.billing.jaxrs.json.AccountJson;
 import org.killbill.billing.jaxrs.json.AccountTimelineJson;
@@ -97,6 +104,7 @@ import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.audit.AccountAuditLogs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.billing.util.config.JaxrsConfig;
 import org.killbill.billing.util.config.PaymentConfig;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.tag.ControlTagType;
@@ -131,6 +139,8 @@ public class AccountResource extends JaxRsResourceBase {
     private final InvoicePaymentApi invoicePaymentApi;
     private final OverdueInternalApi overdueApi;
     private final PaymentConfig paymentConfig;
+    private final JaxrsExecutors jaxrsExecutors;
+    private final JaxrsConfig jaxrsConfig;
 
     @Inject
     public AccountResource(final JaxrsUriBuilder uriBuilder,
@@ -145,6 +155,8 @@ public class AccountResource extends JaxRsResourceBase {
                            final OverdueInternalApi overdueApi,
                            final Clock clock,
                            final PaymentConfig paymentConfig,
+                           final JaxrsExecutors jaxrsExecutors,
+                           final JaxrsConfig jaxrsConfig,
                            final Context context) {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountApi, paymentApi, clock, context);
         this.subscriptionApi = subscriptionApi;
@@ -152,6 +164,8 @@ public class AccountResource extends JaxRsResourceBase {
         this.invoicePaymentApi = invoicePaymentApi;
         this.overdueApi = overdueApi;
         this.paymentConfig = paymentConfig;
+        this.jaxrsExecutors = jaxrsExecutors;
+        this.jaxrsConfig = jaxrsConfig;
     }
 
     @Timed
@@ -353,6 +367,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.INTERNAL_SERVER_ERROR).build();
     }
 
+
     @Timed
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TIMELINE)
@@ -362,28 +377,127 @@ public class AccountResource extends JaxRsResourceBase {
                            @ApiResponse(code = 404, message = "Account not found")})
     public Response getAccountTimeline(@PathParam("accountId") final String accountIdString,
                                        @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
-                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException, PaymentApiException, SubscriptionApiException {
+                                       @QueryParam(QUERY_PARALLEL) @DefaultValue("false") final Boolean parallel,
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws AccountApiException, PaymentApiException, SubscriptionApiException, InvoiceApiException {
         final TenantContext tenantContext = context.createContext(request);
 
         final UUID accountId = UUID.fromString(accountIdString);
         final Account account = accountUserApi.getAccountById(accountId, tenantContext);
 
-        // Get the invoices
-        final List<Invoice> invoices = invoiceApi.getInvoicesByAccount(account.getId(), tenantContext);
+        final Callable<List<SubscriptionBundle>> bundlesCallable = new Callable<List<SubscriptionBundle>>() {
+            @Override
+            public List<SubscriptionBundle> call() throws Exception {
+                return subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), tenantContext);
+            }
+        };
+        final Callable<List<Invoice>> invoicesCallable = new Callable<List<Invoice>>() {
+            @Override
+            public List<Invoice> call() throws Exception {
+                return invoiceApi.getInvoicesByAccount(account.getId(), tenantContext);
+            }
+        };
+        final Callable<List<InvoicePayment>> invoicePaymentsCallable = new Callable<List<InvoicePayment>>() {
+            @Override
+            public List<InvoicePayment> call() throws Exception {
+                return invoicePaymentApi.getInvoicePaymentsByAccount(accountId, tenantContext);
+            }
+        };
+        final Callable<List<Payment>> paymentsCallable = new Callable<List<Payment>>() {
+            @Override
+            public List<Payment> call() throws Exception {
+                return paymentApi.getAccountPayments(accountId, false, ImmutableList.<PluginProperty>of(), tenantContext);
+            }
+        };
+        final Callable<AccountAuditLogs> auditsCallable = new Callable<AccountAuditLogs>() {
+            @Override
+            public AccountAuditLogs call() throws Exception {
+                return auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
+            }
+        };
+
+        final AccountTimelineJson json;
+
+        List<Invoice> invoices = null;
+        List<SubscriptionBundle> bundles = null;
+        List<InvoicePayment> invoicePayments = null;
+        List<Payment> payments = null;
+        AccountAuditLogs accountAuditLogs = null;
+
+        if (parallel) {
+
+            final ExecutorService executor = jaxrsExecutors.getJaxrsExecutorService();
+            final Future<List<SubscriptionBundle>> futureBundlesCallable = executor.submit(bundlesCallable);
+            final Future<List<Invoice>> futureInvoicesCallable = executor.submit(invoicesCallable);
+            final Future<List<InvoicePayment>> futureInvoicePaymentsCallable = executor.submit(invoicePaymentsCallable);
+            final Future<List<Payment>> futurePaymentsCallable = executor.submit(paymentsCallable);
+            final Future<AccountAuditLogs> futureAuditsCallable = executor.submit(auditsCallable);
+
+            try {
+                long ini = System.currentTimeMillis();
+                do {
+                    bundles = (bundles == null) ? runCallableAndHandleTimeout(futureBundlesCallable, 100) : bundles;
+                    invoices = (invoices == null) ? runCallableAndHandleTimeout(futureInvoicesCallable, 100) : invoices;
+                    invoicePayments = (invoicePayments == null) ? runCallableAndHandleTimeout(futureInvoicePaymentsCallable, 100) : invoicePayments;
+                    payments = (payments == null) ? runCallableAndHandleTimeout(futurePaymentsCallable, 100) : payments;
+                    accountAuditLogs = (accountAuditLogs == null) ? runCallableAndHandleTimeout(futureAuditsCallable, 100) : accountAuditLogs;
+                } while ((System.currentTimeMillis() - ini < jaxrsConfig.getJaxrsTimeout().getMillis()) &&
+                         (bundles == null || invoices == null || invoicePayments == null || payments == null || accountAuditLogs == null));
+
+                if (bundles == null || invoices == null || invoicePayments == null || payments == null || accountAuditLogs == null) {
+                    Response.status(Status.SERVICE_UNAVAILABLE).build();
+                }
+            } catch (InterruptedException e) {
+                handleCallableException(e, ImmutableList.<Future>of(futureBundlesCallable, futureInvoicesCallable, futureInvoicePaymentsCallable, futurePaymentsCallable, futureAuditsCallable));
+            } catch (ExecutionException e) {
+                handleCallableException(e.getCause(), ImmutableList.<Future>of(futureBundlesCallable, futureInvoicesCallable, futureInvoicePaymentsCallable, futurePaymentsCallable, futureAuditsCallable));
+            }
 
-        // Get the payments
-        final List<Payment> payments = paymentApi.getAccountPayments(accountId, false, ImmutableList.<PluginProperty>of(), tenantContext);
+        } else {
+            try {
+                invoices = invoicesCallable.call();
+                payments = paymentsCallable.call();
+                bundles = bundlesCallable.call();
+                accountAuditLogs = auditsCallable.call();
+                invoicePayments = invoicePaymentsCallable.call();
+            } catch (Exception e) {
+                handleCallableException(e);
+            }
+        }
 
-        // Get the bundles
-        final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), tenantContext);
+        json = new AccountTimelineJson(account, invoices, payments, invoicePayments, bundles, accountAuditLogs);
+        return Response.status(Status.OK).entity(json).build();
+    }
 
-        // Get all audit logs
-        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(accountId, auditMode.getLevel(), tenantContext);
+    private <T> T runCallableAndHandleTimeout(final Future<T> future, final long timeoutMsec) throws ExecutionException, InterruptedException {
+        try {
+            return future.get(timeoutMsec, TimeUnit.MILLISECONDS);
+        } catch (TimeoutException e) {
+            return null;
+        }
+    }
 
-        final List<InvoicePayment> invoicePayments = invoicePaymentApi.getInvoicePaymentsByAccount(accountId, tenantContext);
-        final AccountTimelineJson json = new AccountTimelineJson(account, invoices, payments, invoicePayments, bundles,
-                                                                 accountAuditLogs);
-        return Response.status(Status.OK).entity(json).build();
+    private void handleCallableException(final Throwable causeOrException, final List<Future> toBeCancelled) throws AccountApiException, SubscriptionApiException, PaymentApiException, InvoiceApiException {
+        for (final Future f : toBeCancelled) {
+            f.cancel(true);
+        }
+        handleCallableException(causeOrException);
+    }
+
+    private void handleCallableException(final Throwable causeOrException) throws AccountApiException, SubscriptionApiException, PaymentApiException, InvoiceApiException {
+        if (causeOrException instanceof AccountApiException) {
+            throw (AccountApiException) causeOrException;
+        } else if (causeOrException instanceof SubscriptionApiException) {
+            throw (SubscriptionApiException) causeOrException;
+        } else if (causeOrException instanceof InvoiceApiException) {
+            throw (InvoiceApiException) causeOrException;
+        } else if (causeOrException instanceof PaymentApiException) {
+            throw (PaymentApiException) causeOrException;
+        } else {
+            if (causeOrException instanceof InterruptedException) {
+                Thread.currentThread().interrupt();
+            }
+            throw new RuntimeException(causeOrException.getMessage(), causeOrException);
+        }
     }
 
     /*
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index 076fd55..7a97f65 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -116,6 +116,8 @@ public interface JaxrsResource {
 
     public static final String QUERY_AUDIT = "audit";
 
+    public static final String QUERY_PARALLEL = "parallel";
+
     public static final String QUERY_NOTIFICATION_CALLBACK = "cb";
 
     public static final String PAGINATION = "pagination";
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 9f76e4f..8e37e12 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
@@ -76,6 +76,7 @@ public class KillbillGuiceListener extends KillbillPlatformGuiceListener {
 
         // In order to use the GZIPContentEncodingFilter, the jersey param "com.sun.jersey.config.feature.logging.DisableEntitylogging"
         // must not be set to false.
+
         if (Boolean.valueOf(System.getProperty(ENABLE_HTTP_GZIP_RESPONSES, "false"))) {
             logger.info("Enable http gzip responses");
             builder.addJerseyFilter(GZIPContentEncodingFilter.class.getName());
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 a67a7d5..7322ef0 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
@@ -26,6 +26,7 @@ import org.killbill.billing.catalog.glue.CatalogModule;
 import org.killbill.billing.currency.glue.CurrencyModule;
 import org.killbill.billing.entitlement.glue.DefaultEntitlementModule;
 import org.killbill.billing.invoice.glue.DefaultInvoiceModule;
+import org.killbill.billing.jaxrs.glue.DefaultJaxrsModule;
 import org.killbill.billing.jaxrs.resources.AccountResource;
 import org.killbill.billing.jaxrs.resources.AdminResource;
 import org.killbill.billing.jaxrs.resources.BundleResource;
@@ -161,6 +162,7 @@ public class KillbillServerModule extends KillbillPlatformModule {
         install(new TemplateModule(configSource));
         install(new DefaultTenantModule(configSource));
         install(new UsageModule(configSource));
+        install(new DefaultJaxrsModule(configSource));
     }
 
     protected void configureResources() {
diff --git a/util/src/main/java/org/killbill/billing/util/config/JaxrsConfig.java b/util/src/main/java/org/killbill/billing/util/config/JaxrsConfig.java
new file mode 100644
index 0000000..a8583e8
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/config/JaxrsConfig.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.util.config;
+
+import org.skife.config.Config;
+import org.skife.config.Default;
+import org.skife.config.Description;
+import org.skife.config.TimeSpan;
+
+public interface JaxrsConfig extends KillbillConfig {
+
+    @Config("org.killbill.jaxrs.threads.pool.nb")
+    @Default("30")
+    @Description("Number of threads for jaxrs executor")
+    int getJaxrsThreadNb();
+
+    @Config("org.killbill.jaxrs.timeout")
+    @Default("30s")
+    @Description("Total timeout for all callables associated to a given api call (parallel mode)")
+    TimeSpan getJaxrsTimeout();
+
+}