Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / tools / development / RequestThreadFactory.java
blobeca56924b9f1d908812534690229a95cf6e52759
1 // Copyright 2011 Google Inc. All Rights Reserved.
3 package com.google.appengine.tools.development;
5 import com.google.apphosting.api.ApiProxy;
7 import java.security.AccessControlContext;
8 import java.security.AccessController;
9 import java.security.PrivilegedAction;
10 import java.util.Date;
11 import java.util.Map;
12 import java.util.concurrent.ThreadFactory;
13 import java.util.logging.Level;
14 import java.util.logging.Logger;
16 /**
17 * This {@link ThreadFactory} creates {@link Thread} objects that
18 * replicate the current request's environment, and are interrupted
19 * when the current request completes.
22 public class RequestThreadFactory implements ThreadFactory {
23 private static final Logger logger = Logger.getLogger(RequestThreadFactory.class.getName());
25 private static final int THREAD_STARTUP_LATENCY_MS = 20;
27 private static final int ONLINE_REQUEST_DEADLINE_MS = 60000;
28 private static final int OFFLINE_REQUEST_DEADLINE_MS = 600000;
30 @Override
31 public Thread newThread(final Runnable runnable) {
32 final boolean callerNativeMode = DevSocketImplFactory.isNativeSocketMode();
34 final AccessControlContext context = AccessController.getContext();
35 return AccessController.doPrivileged(new PrivilegedAction<Thread>() {
36 @Override
37 public Thread run() {
38 final ApiProxy.Environment environment = ApiProxy.getCurrentEnvironment();
39 DevSocketImplFactory.setSocketNativeMode(callerNativeMode);
41 Thread thread = new Thread() {
42 /**
43 * If the thread is started, install a
44 * {@link RequestEndListener} to interrupt the thread
45 * at the end of the request. We don't yet enforce
46 * request deadlines in the DevAppServer so we don't
47 * need to handle other interrupt cases yet.
49 @Override
50 public void start() {
51 try {
52 Thread.sleep(THREAD_STARTUP_LATENCY_MS);
53 } catch (InterruptedException ex) {
54 logger.log(Level.INFO, "Interrupted while simulating thread startup latency", ex);
55 Thread.currentThread().interrupt();
57 super.start();
58 final Thread thread = this;
59 RequestEndListenerHelper.register(new RequestEndListener() {
60 @Override
61 public void onRequestEnd(ApiProxy.Environment environment) {
62 if (thread.isAlive()) {
63 logger.info("Interrupting request thread: " + thread);
64 thread.interrupt();
65 logger.info("Waiting up to 100ms for thread to complete: " + thread);
66 try {
67 thread.join(100);
68 } catch (InterruptedException ex) {
69 logger.info("Interrupted while waiting.");
71 if (thread.isAlive()) {
72 logger.info("Interrupting request thread again: " + thread);
73 thread.interrupt();
74 long remaining = getRemainingDeadlineMillis(environment);
75 logger.info("Waiting up to " + remaining +
76 " ms for thread to complete: " + thread);
77 try {
78 thread.join(remaining);
79 } catch (InterruptedException ex) {
80 logger.info("Interrupted while waiting.");
82 if (thread.isAlive()) {
83 Throwable stack = new Throwable();
84 stack.setStackTrace(thread.getStackTrace());
85 logger.log(Level.SEVERE,
86 "Thread left running: " + thread + ". " +
87 "In production this will cause the request to fail.",
88 stack);
93 });
96 @Override
97 public void run() {
98 ApiProxy.setEnvironmentForCurrentThread(environment);
99 AccessController.doPrivileged(new PrivilegedAction<Object>() {
100 @Override
101 public Object run() {
102 runnable.run();
103 return null;
105 }, context);
108 System.setProperty("devappserver-thread-" + thread.getName(), "true");
109 return thread;
114 private long getRemainingDeadlineMillis(ApiProxy.Environment environment) {
115 int requestTimeMillis;
116 Map<String, Object> attributes = environment.getAttributes();
117 Date startTime = (Date) attributes.get(LocalEnvironment.START_TIME_ATTR);
118 if (startTime != null) {
119 startTime = new Date();
121 Boolean offline = (Boolean) attributes.get(ApiProxyLocalImpl.IS_OFFLINE_REQUEST_KEY);
122 if (offline != null && offline) {
123 requestTimeMillis = OFFLINE_REQUEST_DEADLINE_MS;
124 } else {
125 requestTimeMillis = ONLINE_REQUEST_DEADLINE_MS;
127 return requestTimeMillis - (System.currentTimeMillis() - startTime.getTime());