Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / tools / development / AbstractContainerService.java
blob367008d1f264d1ef8d939e7b6fe12928af5ad4cb
1 // Copyright 2008 Google Inc. All Rights Reserved.
3 package com.google.appengine.tools.development;
5 import com.google.appengine.api.log.dev.LocalLogService;
6 import com.google.appengine.tools.development.ApplicationConfigurationManager.ServerConfigurationHandle;
7 import com.google.appengine.tools.info.SdkImplInfo;
8 import com.google.apphosting.api.ApiProxy;
9 import com.google.apphosting.utils.config.AppEngineWebXml;
10 import com.google.apphosting.utils.config.ClassPathBuilder;
11 import com.google.apphosting.utils.config.WebModule;
12 import com.google.apphosting.utils.config.WebXml;
13 import com.google.common.base.Joiner;
14 import com.google.common.collect.ImmutableMap;
16 import java.io.File;
17 import java.io.IOException;
18 import java.net.InetAddress;
19 import java.net.URL;
20 import java.net.UnknownHostException;
21 import java.security.Permissions;
22 import java.util.Collection;
23 import java.util.Map;
24 import java.util.concurrent.CountDownLatch;
25 import java.util.logging.Handler;
26 import java.util.logging.Level;
27 import java.util.logging.Logger;
29 import javax.annotation.concurrent.GuardedBy;
31 /**
32 * Common implementation for the {@link ContainerService} interface.
34 * There should be no reference to any third-party servlet container from here.
37 abstract class AbstractContainerService implements ContainerService {
39 private static final Logger log = Logger.getLogger(AbstractContainerService.class.getName());
41 protected static final String _AH_URL_RELOAD = "/_ah/reloadwebapp";
43 private static final String USER_CODE_CLASSPATH_MANAGER_PROP =
44 "devappserver.userCodeClasspathManager";
46 private static final String USER_CODE_CLASSPATH = USER_CODE_CLASSPATH_MANAGER_PROP + ".classpath";
47 private static final String USER_CODE_REQUIRES_WEB_INF =
48 USER_CODE_CLASSPATH_MANAGER_PROP + ".requiresWebInf";
50 protected ServerConfigurationHandle serverConfigurationHandle;
51 protected String devAppServerVersion;
52 protected File appDir;
53 protected File externalResourceDir;
55 /**
56 * The location of web.xml. If not provided, defaults to
57 * <appDir>/WEB-INF/web.xml
59 protected File webXmlLocation;
61 /**
62 * The hostname on which the server is listening for http requests.
64 protected String hostName;
66 /**
67 * The network address on which the server is listening for http requests.
69 protected String address;
71 /**
72 * The port on which the server is listening for http requests.
74 protected int port;
76 /**
77 * The 0 based index for this instance or {@link LocalEnvironment#MAIN_INSTANCE}.
79 protected int instance;
81 /**
82 * A reference to the parent DevAppServer that configured this container.
84 protected DevAppServer devAppServer;
86 protected AppEngineWebXml appEngineWebXml;
88 protected WebXml webXml;
90 @GuardedBy("AbstractContainerService.class")
91 private static SystemPropertiesManager systemPropertiesManager;
93 /**
94 * Latch that will open once the server is fully initialized.
96 private CountDownLatch serverInitLatch;
98 /**
99 * Not initialized until {@link #startup()} has been called.
101 protected ApiProxyLocal apiProxyLocal;
103 protected UserCodeClasspathManager userCodeClasspathManager;
105 protected ServersFilterHelper serversFilterHelper;
107 @Override
108 public final LocalServerEnvironment configure(String devAppServerVersion, final String address,
109 int port, final ServerConfigurationHandle serverConfigurationHandle, File externalResourceDir,
110 Map<String, Object> containerConfigProperties, int instance, DevAppServer devAppServer) {
111 this.devAppServerVersion = devAppServerVersion;
112 this.serverConfigurationHandle = serverConfigurationHandle;
113 extractFieldsFromWebModule(serverConfigurationHandle.getModule());
114 this.externalResourceDir = externalResourceDir;
115 this.address = address;
116 this.port = port;
117 this.serverInitLatch = new CountDownLatch(1);
118 this.hostName = "localhost";
119 this.devAppServer = devAppServer;
120 if ("0.0.0.0".equals(address)) {
121 try {
122 InetAddress localhost = InetAddress.getLocalHost();
123 this.hostName = localhost.getHostName();
124 } catch (UnknownHostException ex) {
125 log.log(Level.WARNING,
126 "Unable to determine hostname - defaulting to localhost.");
130 this.userCodeClasspathManager = newUserCodeClasspathProvider(containerConfigProperties);
131 this.serversFilterHelper = (ServersFilterHelper)
132 containerConfigProperties.get(DevAppServerImpl.SERVERS_FILTER_HELPER_PROPERTY);
133 this.instance = instance;
134 return new LocalServerEnvironment() {
135 @Override
136 public File getAppDir() {
137 return serverConfigurationHandle.getModule().getApplicationDirectory();
140 @Override
141 public String getAddress() {
142 return address;
145 @Override
146 public String getHostName() {
147 return hostName;
150 @Override
151 public int getPort() {
152 return AbstractContainerService.this.port;
155 @Override
156 public void waitForServerToStart() throws InterruptedException {
157 serverInitLatch.await();
160 @Override
161 public boolean simulateProductionLatencies() {
162 return false;
165 @Override
166 public boolean enforceApiDeadlines() {
167 return !Boolean.getBoolean("com.google.appengine.disable_api_deadlines");
173 * @param webModule
175 protected void extractFieldsFromWebModule(WebModule webModule) {
176 this.appDir = webModule.getApplicationDirectory();
177 this.webXml = webModule.getWebXml();
178 this.webXmlLocation = webModule.getWebXmlFile();
179 this.appEngineWebXml = webModule.getAppEngineWebXml();
183 * Constructs a {@link UserCodeClasspathManager} from the given properties.
185 private static UserCodeClasspathManager newUserCodeClasspathProvider(
186 Map<String, Object> containerConfigProperties) {
187 if (containerConfigProperties.containsKey(USER_CODE_CLASSPATH_MANAGER_PROP)) {
188 @SuppressWarnings("unchecked")
189 final Map<String, Object> userCodeClasspathManagerProps =
190 (Map<String, Object>) containerConfigProperties.get(USER_CODE_CLASSPATH_MANAGER_PROP);
191 return new UserCodeClasspathManager() {
192 @SuppressWarnings("unchecked")
193 @Override
194 public Collection<URL> getUserCodeClasspath(File root) {
195 return (Collection<URL>) userCodeClasspathManagerProps.get(USER_CODE_CLASSPATH);
198 @Override
199 public boolean requiresWebInf() {
200 return (Boolean) userCodeClasspathManagerProps.get(USER_CODE_REQUIRES_WEB_INF);
204 return new WebAppUserCodeClasspathManager();
207 protected void installLoggingServiceHandler() {
208 Logger root = Logger.getLogger("");
210 ApiProxyLocal proxy = (ApiProxyLocal) ApiProxy.getDelegate();
211 LocalLogService logService = (LocalLogService) proxy.getService(LocalLogService.PACKAGE);
212 root.addHandler(logService.getLogHandler());
214 Handler[] handlers = root.getHandlers();
215 if (handlers != null) {
216 for (Handler handler : handlers) {
217 handler.setLevel(Level.FINEST);
222 @Override
223 public final void createConnection() throws Exception {
224 connectContainer();
227 @Override
228 public final void startup() throws Exception {
229 apiProxyLocal = (ApiProxyLocal) ApiProxy.getDelegate();
230 initContext();
231 installLoggingServiceHandler();
232 if (appEngineWebXml == null) {
233 throw new IllegalStateException("initContext failed to initialize appEngineWebXml.");
236 startContainer();
237 startHotDeployScanner();
238 serverInitLatch.countDown();
241 @Override
242 public final void shutdown() throws Exception {
243 stopHotDeployScanner();
244 stopContainer();
245 serverConfigurationHandle.restoreSystemProperties();
248 @Override
249 public Map<String, String> getServiceProperties() {
250 return ImmutableMap.of("appengine.dev.inbound-services",
251 Joiner.on(",").useForNull("null").join(appEngineWebXml.getInboundServices()));
255 * Set up the webapp context in a container specific way.
256 * <p>Note that {@link #initContext()} is required to call
257 * {@link #installLocalInitializationEnvironment()} for the service to be correctly
258 * initialized.
260 * @return the effective webapp directory.
262 protected abstract File initContext() throws IOException;
265 * Creates the servlet container's network connections.
267 protected abstract void connectContainer() throws Exception;
270 * Start up the servlet container runtime.
272 protected abstract void startContainer() throws Exception;
275 * Stop the servlet container runtime.
277 protected abstract void stopContainer() throws Exception;
280 * Start up the hot-deployment scanner.
282 protected abstract void startHotDeployScanner() throws Exception;
285 * Stop the hot-deployment scanner.
287 protected abstract void stopHotDeployScanner() throws Exception;
290 * Re-deploy the current webapp context in a container specific way,
291 * while taking into account possible appengine-web.xml change too,
292 * without restarting the server.
294 protected abstract void reloadWebApp() throws Exception;
296 @Override
297 public String getAddress() {
298 return address;
301 @Override
302 public AppEngineWebXml getAppEngineWebXmlConfig(){
303 return appEngineWebXml;
306 @Override
307 public int getPort() {
308 return port;
311 @Override
312 public String getHostName() {
313 return hostName;
316 protected Permissions getUserPermissions() {
317 return appEngineWebXml.getUserPermissions();
321 * Installs a {@link LocalInitializationEnvironment} with
322 * {@link ApiProxy#setEnvironmentForCurrentThread}.
323 * <p>
324 * Filters and servlets get initialized when we call server.start(). If any of
325 * those filters and servlets need access to the current execution environment
326 * they'll call ApiProxy.getCurrentEnvironment(). We set a special initialization
327 * environment so that there is an environment available when this happens.
329 protected void installLocalInitializationEnvironment() {
331 ApiProxy.setEnvironmentForCurrentThread(new LocalInitializationEnvironment(
332 appEngineWebXml.getAppId(), WebModule.getServerName(appEngineWebXml),
333 appEngineWebXml.getMajorVersionId(), instance));
336 /** Returns {@code true} if appengine-web.xml <sessions-enabled> is true. */
337 protected boolean isSessionsEnabled() {
338 return appEngineWebXml.getSessionsEnabled();
342 * Gets all of the URLs that should be added to the classpath for an
343 * application located at {@code root}.
345 protected URL[] getClassPathForApp(File root) {
346 ClassPathBuilder classPathBuilder =
347 new ClassPathBuilder(appEngineWebXml.getClassLoaderConfig());
349 classPathBuilder.addUrls(SdkImplInfo.getAgentRuntimeLibs());
351 classPathBuilder.addUrls(userCodeClasspathManager.getUserCodeClasspath(root));
352 classPathBuilder.addUrls(SdkImplInfo.getUserJspLibs());
353 classPathBuilder.addUrls(SdkImplInfo.getWebApiToolLibs());
354 return getUrls(classPathBuilder);
357 private static URL[] getUrls(ClassPathBuilder classPathBuilder) {
358 URL[] urls = classPathBuilder.getUrls();
359 String message = classPathBuilder.getLogMessage();
360 if (!message.isEmpty()) {
361 log.warning(message);
363 return urls;
367 * A fake {@link LocalEnvironment} implementation that is used during the
368 * initialization of the Development AppServer.
370 public static class LocalInitializationEnvironment extends LocalEnvironment {
371 public LocalInitializationEnvironment(String appId, String serverName, String majorVersionId,
372 int instance) {
373 super(appId, serverName, majorVersionId, instance, null);
376 @Override
377 public String getEmail() {
378 return null;
381 @Override
382 public boolean isLoggedIn() {
383 return false;
386 @Override
387 public boolean isAdmin() {
388 return false;