diff --git a/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java
index f2c1bb4e3..1b1ded997 100644
--- a/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java
+++ b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java
@@ -27,6 +27,7 @@
import com.netflix.hystrix.HystrixEventType;
import com.netflix.hystrix.HystrixThreadPoolKey;
import com.netflix.hystrix.HystrixThreadPoolMetrics;
+import com.netflix.hystrix.util.PlatformSpecific;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rx.functions.Func0;
@@ -74,7 +75,15 @@ public class HystrixMetricsPoller {
*/
public HystrixMetricsPoller(MetricsAsJsonPollerListener listener, int delay) {
this.listener = listener;
- executor = new ScheduledThreadPoolExecutor(1, new MetricsPollerThreadFactory());
+
+ ThreadFactory threadFactory = null;
+ if (!PlatformSpecific.isAppEngine()) {
+ threadFactory = new MetricsPollerThreadFactory();
+ } else {
+ threadFactory = PlatformSpecific.getAppEngineThreadFactory();
+ }
+
+ executor = new ScheduledThreadPoolExecutor(1, threadFactory);
this.delay = delay;
}
diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java
index 3d5f92c64..a95668896 100644
--- a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java
+++ b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java
@@ -15,6 +15,13 @@
*/
package com.netflix.hystrix.strategy.concurrency;
+import com.netflix.hystrix.HystrixCommand;
+import com.netflix.hystrix.HystrixThreadPool;
+import com.netflix.hystrix.HystrixThreadPoolKey;
+import com.netflix.hystrix.strategy.HystrixPlugins;
+import com.netflix.hystrix.strategy.properties.HystrixProperty;
+import com.netflix.hystrix.util.PlatformSpecific;
+
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
@@ -24,12 +31,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
-import com.netflix.hystrix.HystrixCommand;
-import com.netflix.hystrix.HystrixThreadPool;
-import com.netflix.hystrix.HystrixThreadPoolKey;
-import com.netflix.hystrix.strategy.HystrixPlugins;
-import com.netflix.hystrix.strategy.properties.HystrixProperty;
-
/**
* Abstract class for defining different behavior or implementations for concurrency related aspects of the system with default implementations.
*
@@ -70,17 +71,24 @@ public abstract class HystrixConcurrencyStrategy {
* @return instance of {@link ThreadPoolExecutor}
*/
public ThreadPoolExecutor getThreadPool(final HystrixThreadPoolKey threadPoolKey, HystrixProperty corePoolSize, HystrixProperty maximumPoolSize, HystrixProperty keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
- return new ThreadPoolExecutor(corePoolSize.get(), maximumPoolSize.get(), keepAliveTime.get(), unit, workQueue, new ThreadFactory() {
+ ThreadFactory threadFactory = null;
+ if (!PlatformSpecific.isAppEngine()) {
+ threadFactory = new ThreadFactory() {
+ protected final AtomicInteger threadNumber = new AtomicInteger(0);
- protected final AtomicInteger threadNumber = new AtomicInteger(0);
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet());
+ thread.setDaemon(true);
+ return thread;
+ }
+
+ };
+ } else {
+ threadFactory = PlatformSpecific.getAppEngineThreadFactory();
+ }
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet());
- thread.setDaemon(true);
- return thread;
- }
- });
+ return new ThreadPoolExecutor(corePoolSize.get(), maximumPoolSize.get(), keepAliveTime.get(), unit, workQueue, threadFactory);
}
/**
diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/util/HystrixTimer.java b/hystrix-core/src/main/java/com/netflix/hystrix/util/HystrixTimer.java
index 123eea989..722b1dff1 100644
--- a/hystrix-core/src/main/java/com/netflix/hystrix/util/HystrixTimer.java
+++ b/hystrix-core/src/main/java/com/netflix/hystrix/util/HystrixTimer.java
@@ -15,6 +15,13 @@
*/
package com.netflix.hystrix.util;
+import com.netflix.hystrix.HystrixCollapser;
+import com.netflix.hystrix.HystrixCommand;
+import com.netflix.hystrix.strategy.HystrixPlugins;
+import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.concurrent.ScheduledFuture;
@@ -24,14 +31,6 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
-import com.netflix.hystrix.strategy.HystrixPlugins;
-import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.netflix.hystrix.HystrixCollapser;
-import com.netflix.hystrix.HystrixCommand;
-
/**
* Timer used by {@link HystrixCommand} to timeout async executions and {@link HystrixCollapser} to trigger batch executions.
*/
@@ -153,17 +152,24 @@ public void initialize() {
HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
int coreSize = propertiesStrategy.getTimerThreadPoolProperties().getCorePoolSize().get();
- executor = new ScheduledThreadPoolExecutor(coreSize, new ThreadFactory() {
- final AtomicInteger counter = new AtomicInteger();
-
- @Override
- public Thread newThread(Runnable r) {
- Thread thread = new Thread(r, "HystrixTimer-" + counter.incrementAndGet());
- thread.setDaemon(true);
- return thread;
- }
+ ThreadFactory threadFactory = null;
+ if (!PlatformSpecific.isAppEngine()) {
+ threadFactory = new ThreadFactory() {
+ final AtomicInteger counter = new AtomicInteger();
+
+ @Override
+ public Thread newThread(Runnable r) {
+ Thread thread = new Thread(r, "HystrixTimer-" + counter.incrementAndGet());
+ thread.setDaemon(true);
+ return thread;
+ }
+
+ };
+ } else {
+ threadFactory = PlatformSpecific.getAppEngineThreadFactory();
+ }
- });
+ executor = new ScheduledThreadPoolExecutor(coreSize, threadFactory);
initialized = true;
}
diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/util/PlatformSpecific.java b/hystrix-core/src/main/java/com/netflix/hystrix/util/PlatformSpecific.java
new file mode 100644
index 000000000..263b63667
--- /dev/null
+++ b/hystrix-core/src/main/java/com/netflix/hystrix/util/PlatformSpecific.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2016 Netflix, Inc.
+ *
+ * Licensed 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 com.netflix.hystrix.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.ThreadFactory;
+
+public class PlatformSpecific {
+ private final boolean isAppEngine;
+
+ private static PlatformSpecific INSTANCE = new PlatformSpecific();
+
+ private PlatformSpecific() {
+ isAppEngine = determineAppEngineReflectively();
+ }
+
+ public static boolean isAppEngine() {
+ return INSTANCE.isAppEngine;
+ }
+
+ /*
+ * This detection mechanism is from Guava - specifically
+ * http://docs.guava-libraries.googlecode.com/git/javadoc/src-html/com/google/common/util/concurrent/MoreExecutors.html#line.766
+ */
+ private static boolean determineAppEngineReflectively() {
+ if (System.getProperty("com.google.appengine.runtime.environment") == null) {
+ return false;
+ }
+ try {
+ // If the current environment is null, we're not inside AppEngine.
+ return Class.forName("com.google.apphosting.api.ApiProxy")
+ .getMethod("getCurrentEnvironment")
+ .invoke(null) != null;
+ } catch (ClassNotFoundException e) {
+ // If ApiProxy doesn't exist, we're not on AppEngine at all.
+ return false;
+ } catch (InvocationTargetException e) {
+ // If ApiProxy throws an exception, we're not in a proper AppEngine environment.
+ return false;
+ } catch (IllegalAccessException e) {
+ // If the method isn't accessible, we're not on a supported version of AppEngine;
+ return false;
+ } catch (NoSuchMethodException e) {
+ // If the method doesn't exist, we're not on a supported version of AppEngine;
+ return false;
+ }
+ }
+
+ public static ThreadFactory getAppEngineThreadFactory() {
+ try {
+ return (ThreadFactory) Class.forName("com.google.appengine.api.ThreadManager")
+ .getMethod("currentRequestThreadFactory")
+ .invoke(null);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getCause());
+ }
+ }
+}