1.9.30 sync.
[gae.git] / java / src / main / com / google / appengine / tools / development / DevAppServerMain.java
blobbf065e7746f6b3385e74fbfd16e7966e4214da7a
1 // Copyright 2008 Google Inc. All Rights Reserved.
3 package com.google.appengine.tools.development;
5 import com.google.appengine.tools.info.SdkInfo;
6 import com.google.appengine.tools.info.UpdateCheck;
7 import com.google.appengine.tools.util.Action;
8 import com.google.appengine.tools.util.Option;
9 import com.google.appengine.tools.util.Parser;
10 import com.google.appengine.tools.util.Parser.ParseResult;
11 import com.google.apphosting.utils.config.GenerationDirectory;
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.collect.ImmutableList;
15 import java.io.File;
16 import java.io.PrintStream;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.List;
20 import java.util.Map;
22 /**
23 * The command-line entry point for DevAppServer.
26 public class DevAppServerMain extends SharedMain {
28 public static final String EXTERNAL_RESOURCE_DIR_ARG = "external_resource_dir";
29 public static final String GENERATE_WAR_ARG = "generate_war";
30 public static final String GENERATED_WAR_DIR_ARG = "generated_war_dir";
32 private static final String DEFAULT_RDBMS_PROPERTIES_FILE = ".local.rdbms.properties";
33 private static final String RDBMS_PROPERTIES_FILE_SYSTEM_PROPERTY = "rdbms.properties.file";
35 private static final String SYSTEM_PROPERTY_STATIC_MODULE_PORT_NUM_PREFIX =
36 "com.google.appengine.devappserver_module.";
38 private final Action ACTION = new StartAction();
40 private String versionCheckServer = SdkInfo.getDefaultServer();
42 private String address = DevAppServer.DEFAULT_HTTP_ADDRESS;
43 private int port = DevAppServer.DEFAULT_HTTP_PORT;
44 private boolean disableUpdateCheck;
45 private String generatedDirectory = null;
46 private String defaultGcsBucketName = null;
48 /**
49 * Returns the list of built-in {@link Option Options} for the given instance of
50 * {@link DevAppServerMain}.
52 * @param main The instance of {@code DevAppServerMain} for which the built-in options are being
53 * requested. This may be {@code null} if {@link Option#apply()} will never be invoked on
54 * any of the returned {@code Options}.
55 * @return The list of built-in options
57 @VisibleForTesting
58 List<Option> getBuiltInOptions() {
59 List<Option> options = new ArrayList<>();
60 options.addAll(getSharedOptions());
61 options.addAll(Arrays.asList(
62 new Option("s", "server", false) {
63 @Override
64 public void apply() {
65 versionCheckServer = getValue();
67 @Override
68 public List<String> getHelpLines() {
69 return ImmutableList.of(
70 " --server=SERVER The server to use to determine the latest",
71 " -s SERVER SDK version.");
74 new Option("a", "address", false) {
75 @Override
76 public void apply() {
77 address = getValue();
79 @Override
80 public List<String> getHelpLines() {
81 return ImmutableList.of(
82 " --address=ADDRESS The address of the interface on the local machine",
83 " -a ADDRESS to bind to (or 0.0.0.0 for all interfaces).");
86 new Option("p", "port", false) {
87 @Override
88 public void apply() {
89 port = Integer.valueOf(getValue());
91 @Override
92 public List<String> getHelpLines() {
93 return ImmutableList.of(
94 " --port=PORT The port number to bind to on the local machine.",
95 " -p PORT");
98 new Option(null, "disable_update_check", true) {
99 @Override
100 public void apply() {
101 disableUpdateCheck = true;
103 @Override
104 public List<String> getHelpLines() {
105 return ImmutableList.of(
106 " --disable_update_check Disable the check for newer SDK versions.");
109 new Option(null, "generated_dir", false) {
110 @Override
111 public void apply() {
112 generatedDirectory = getValue();
114 @Override
115 public List<String> getHelpLines() {
116 return ImmutableList.of(
117 " --generated_dir=DIR Set the directory where generated files are created.");
120 new Option(null, "default_gcs_bucket", false) {
121 @Override
122 public void apply() {
123 defaultGcsBucketName = getValue();
125 @Override
126 public List<String> getHelpLines() {
127 return ImmutableList.of(
128 " --default_gcs_bucket=NAME Set the default Google Cloud Storage bucket name.");
131 new Option(null, "instance_port", false) {
132 @Override
133 public void apply() {
134 processInstancePorts(getValues());
137 new Option(null, "disable_filesapi_warning", true) {
138 @Override
139 public void apply() {
140 System.setProperty("appengine.disableFilesApiWarning", "true");
143 new Option(null, "enable_filesapi", true) {
144 @Override
145 public void apply() {
146 System.setProperty("appengine.enableFilesApi", "true");
150 return options;
153 private static void processInstancePorts(List<String> optionValues) {
154 for (String optionValue : optionValues) {
155 String[] keyAndValue = optionValue.split("=", 2);
156 if (keyAndValue.length != 2) {
157 reportBadInstancePortValue(optionValue);
160 try {
161 Integer.parseInt(keyAndValue[1]);
162 } catch (NumberFormatException nfe) {
163 reportBadInstancePortValue(optionValue);
166 System.setProperty(
167 SYSTEM_PROPERTY_STATIC_MODULE_PORT_NUM_PREFIX + keyAndValue[0].trim() + ".port",
168 keyAndValue[1].trim());
172 private static void reportBadInstancePortValue(String optionValue) {
173 throw new IllegalArgumentException("Invalid instance_port value " + optionValue);
177 * Builds the complete list of {@link Option Options} for the given instance of
178 * {@link DevAppServerMain}. The list consists of the built-in options.
180 * @param main The instance of {@code DevAppServerMain} for which the options are being requested.
181 * This may be {@code null} if {@link Option#apply()} will never be invoked on any of the
182 * returned {@code Options}.
183 * @return The list of all options
185 private List<Option> buildOptions() {
186 List<Option> options = getBuiltInOptions();
187 return options;
190 public static void main(String[] args) throws Exception {
191 SharedMain.sharedInit();
192 new DevAppServerMain().run(args);
195 public DevAppServerMain() {
198 public void run(String[] args) throws Exception {
199 Parser parser = new Parser();
200 ParseResult result = parser.parseArgs(ACTION, buildOptions(), args);
201 result.applyArgs();
204 @Override
205 public void printHelp(PrintStream out) {
206 out.println("Usage: <dev-appserver> [options] <app directory>");
207 out.println("");
208 out.println("Options:");
209 for (Option option : buildOptions()) {
210 for (String helpString : option.getHelpLines()) {
211 out.println(helpString);
214 out.println(" --jvm_flag=FLAG Pass FLAG as a JVM argument. May be repeated to");
215 out.println(" supply multiple flags.");
218 class StartAction extends Action {
219 StartAction() {
220 super("start");
223 @Override
224 public void apply() {
225 List<String> args = getArgs();
226 try {
227 File externalResourceDir = getExternalResourceDir();
228 if (args.size() != 1) {
229 printHelp(System.err);
230 System.exit(1);
232 File appDir = new File(args.get(0)).getCanonicalFile();
233 validateWarPath(appDir);
235 UpdateCheck updateCheck = new UpdateCheck(versionCheckServer, appDir, true);
236 if (updateCheck.allowedToCheckForUpdates() && !disableUpdateCheck) {
237 updateCheck.maybePrintNagScreen(System.err);
239 updateCheck.checkJavaVersion(System.err);
241 DevAppServer server = new DevAppServerFactory().createDevAppServer(appDir,
242 externalResourceDir, address, port, getNoJavaAgent());
244 Map<String, String> stringProperties = getSystemProperties();
245 setGeneratedDirectory(stringProperties);
246 setRdbmsPropertiesFile(stringProperties, appDir, externalResourceDir);
247 postServerActions(stringProperties);
248 setDefaultGcsBucketName(stringProperties);
249 addPropertyOptionToProperties(stringProperties);
250 server.setServiceProperties(stringProperties);
252 try {
253 server.start().await();
254 } catch (InterruptedException e) {
257 System.out.println("Shutting down.");
258 System.exit(0);
259 } catch (Exception ex) {
260 ex.printStackTrace();
261 System.exit(1);
265 private void setGeneratedDirectory(Map<String, String> stringProperties) {
266 if (generatedDirectory != null) {
267 File dir = new File(generatedDirectory);
268 String error = null;
269 if (dir.exists()) {
270 if (!dir.isDirectory()) {
271 error = generatedDirectory + " is not a directory.";
272 } else if (!dir.canWrite()) {
273 error = generatedDirectory + " is not writable.";
275 } else if (!dir.mkdirs()) {
276 error = "Could not make " + generatedDirectory;
278 if (error != null) {
279 System.err.println(error);
280 System.exit(1);
282 stringProperties.put(GenerationDirectory.GENERATED_DIR_PROPERTY, generatedDirectory);
286 private void setDefaultGcsBucketName(Map<String, String> stringProperties) {
287 if (defaultGcsBucketName != null) {
288 stringProperties.put("appengine.default.gcs.bucket.name", defaultGcsBucketName);
293 * Sets the property named {@link #RDBMS_PROPERTIES_FILE_SYSTEM_PROPERTY} to the default value
294 * {@link #DEFAULT_RDBMS_PROPERTIES_FILE} if the property is not already set and if there is a
295 * file by that name in either {@code appDir} or {@code externalResourceDir}.
297 * @param stringProperties The map in which the value will be set
298 * @param appDir The appDir, aka the WAR dir
299 * @param externalResourceDir the external resource dir, or {@code null} if there is not one.
301 private void setRdbmsPropertiesFile(
302 Map<String, String> stringProperties, File appDir, File externalResourceDir) {
303 if (stringProperties.get(RDBMS_PROPERTIES_FILE_SYSTEM_PROPERTY) != null) {
304 return;
306 File file = findRdbmsPropertiesFile(externalResourceDir);
307 if (file == null) {
308 file = findRdbmsPropertiesFile(appDir);
310 if (file != null) {
311 String path = file.getPath();
312 System.out.println("Reading local rdbms properties from " + path);
313 stringProperties.put(RDBMS_PROPERTIES_FILE_SYSTEM_PROPERTY, path);
318 * Returns the default rdbms properties file in the given dir if it exists.
319 * @param dir The directory in which to look
320 * @return The default rdbs properties file, or {@code null}.
322 private File findRdbmsPropertiesFile(File dir) {
323 File candidate = new File(dir, DEFAULT_RDBMS_PROPERTIES_FILE);
324 if (candidate.isFile() && candidate.canRead()) {
325 return candidate;
327 return null;