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
;
17 import java
.io
.IOException
;
18 import java
.net
.InetAddress
;
20 import java
.net
.UnknownHostException
;
21 import java
.security
.Permissions
;
22 import java
.util
.Collection
;
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
;
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
;
56 * The location of web.xml. If not provided, defaults to
57 * <appDir>/WEB-INF/web.xml
59 protected File webXmlLocation
;
62 * The hostname on which the server is listening for http requests.
64 protected String hostName
;
67 * The network address on which the server is listening for http requests.
69 protected String address
;
72 * The port on which the server is listening for http requests.
77 * The 0 based index for this instance or {@link LocalEnvironment#MAIN_INSTANCE}.
79 protected int instance
;
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
;
94 * Latch that will open once the server is fully initialized.
96 private CountDownLatch serverInitLatch
;
99 * Not initialized until {@link #startup()} has been called.
101 protected ApiProxyLocal apiProxyLocal
;
103 protected UserCodeClasspathManager userCodeClasspathManager
;
105 protected ServersFilterHelper serversFilterHelper
;
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
;
117 this.serverInitLatch
= new CountDownLatch(1);
118 this.hostName
= "localhost";
119 this.devAppServer
= devAppServer
;
120 if ("0.0.0.0".equals(address
)) {
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() {
136 public File
getAppDir() {
137 return serverConfigurationHandle
.getModule().getApplicationDirectory();
141 public String
getAddress() {
146 public String
getHostName() {
151 public int getPort() {
152 return AbstractContainerService
.this.port
;
156 public void waitForServerToStart() throws InterruptedException
{
157 serverInitLatch
.await();
161 public boolean simulateProductionLatencies() {
166 public boolean enforceApiDeadlines() {
167 return !Boolean
.getBoolean("com.google.appengine.disable_api_deadlines");
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")
194 public Collection
<URL
> getUserCodeClasspath(File root
) {
195 return (Collection
<URL
>) userCodeClasspathManagerProps
.get(USER_CODE_CLASSPATH
);
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
);
223 public final void createConnection() throws Exception
{
228 public final void startup() throws Exception
{
229 apiProxyLocal
= (ApiProxyLocal
) ApiProxy
.getDelegate();
231 installLoggingServiceHandler();
232 if (appEngineWebXml
== null) {
233 throw new IllegalStateException("initContext failed to initialize appEngineWebXml.");
237 startHotDeployScanner();
238 serverInitLatch
.countDown();
242 public final void shutdown() throws Exception
{
243 stopHotDeployScanner();
245 serverConfigurationHandle
.restoreSystemProperties();
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
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
;
297 public String
getAddress() {
302 public AppEngineWebXml
getAppEngineWebXmlConfig(){
303 return appEngineWebXml
;
307 public int getPort() {
312 public String
getHostName() {
316 protected Permissions
getUserPermissions() {
317 return appEngineWebXml
.getUserPermissions();
321 * Installs a {@link LocalInitializationEnvironment} with
322 * {@link ApiProxy#setEnvironmentForCurrentThread}.
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
);
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
,
373 super(appId
, serverName
, majorVersionId
, instance
, null);
377 public String
getEmail() {
382 public boolean isLoggedIn() {
387 public boolean isAdmin() {