Revision created by MOE tool push_codebase.
[gae.git] / java / src / main / com / google / appengine / tools / info / SdkInfo.java
blob24d79b162dfd8d9e1f71c1b25ea2145652a16e8f
1 // Copyright 2008 Google Inc. All Rights Reserved.
3 package com.google.appengine.tools.info;
5 import java.io.File;
6 import java.io.FileFilter;
7 import java.net.MalformedURLException;
8 import java.net.URISyntaxException;
9 import java.net.URL;
10 import java.util.ArrayList;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.List;
14 import java.util.SortedMap;
15 import java.util.TreeMap;
17 /**
18 * Retrieves installation information for the App Engine SDK.
21 public class SdkInfo {
23 public static final String SDK_ROOT_PROPERTY = "appengine.sdk.root";
25 private static final String DEFAULT_SERVER = "appengine.google.com";
27 private static boolean isInitialized = false;
28 private static File sdkRoot = null;
29 private static List<File> sharedLibFiles = null;
30 private static List<URL> sharedLibs = null;
31 private static List<File> userLibFiles = null;
32 private static List<URL> userLibs = null;
33 private static SortedMap<String, OptionalLib> optionalUserLibsByName = null;
34 private static SortedMap<String, OptionalLib> optionalToolsLibsByName = null;
35 private static boolean isDevAppServerTest;
37 private static final FileFilter NO_HIDDEN_FILES = new FileFilter() {
38 @Override
39 public boolean accept(File file) {
40 return !file.isHidden();
44 static List<URL> toURLs(List<File> files) {
45 List<URL> urls = new ArrayList<URL>(files.size());
46 for (File file : files) {
47 urls.add(toURL(file));
49 return urls;
52 @SuppressWarnings({"deprecation"})
53 static URL toURL(File file) {
54 try {
55 return file.toURL();
56 } catch (MalformedURLException e) {
57 throw new RuntimeException("Unable get a URL from " + file, e);
61 static List<File> getLibs(File sdkRoot, String libSubDir) {
62 return getLibs(sdkRoot, libSubDir, false);
65 static List<File> getLibsRecursive(File sdkRoot, String libSubDir) {
66 return getLibs(sdkRoot, libSubDir, true);
69 private static List<File> getLibs(File sdkRoot, String libSubDir, boolean recursive) {
70 File subDir = new File(sdkRoot, "lib" + File.separator + libSubDir);
72 if (!subDir.exists()) {
73 throw new IllegalArgumentException("Unable to find " + subDir.getAbsolutePath());
76 List<File> libs = new ArrayList<File>();
77 getLibs(subDir, libs, recursive);
78 return libs;
81 private static void getLibs(File dir, List<File> list, boolean recursive) {
82 for (File f : listFiles(dir)) {
83 if (f.isDirectory() && recursive) {
84 getLibs(f, list, recursive);
85 } else {
86 if (f.getName().endsWith(".jar")) {
87 list.add(f);
93 private static File findSdkRoot() {
94 String explicitRootString = System.getProperty(SDK_ROOT_PROPERTY);
95 if (explicitRootString != null) {
96 return new File(explicitRootString);
99 URL codeLocation = SdkInfo.class.getProtectionDomain().getCodeSource().getLocation();
100 String msg = "Unable to discover the Google App Engine SDK root. This code should be loaded " +
101 "from the SDK directory, but was instead loaded from " + codeLocation + ". Specify " +
102 "-Dappengine.sdk.root to override the SDK location.";
103 File libDir;
104 try {
105 libDir = new File(codeLocation.toURI());
106 } catch (URISyntaxException e) {
107 libDir = new File(codeLocation.getFile());
109 while (!libDir.getName().equals("lib")) {
110 libDir = libDir.getParentFile();
111 if (libDir == null) {
112 throw new RuntimeException(msg);
115 return libDir.getParentFile();
119 * Returns the full paths of all shared libraries for the SDK. Users
120 * should compile against these libraries, but <b>not</b> bundle them
121 * with their web application. These libraries are already included
122 * as part of the App Engine runtime.
124 public static List<URL> getSharedLibs() {
125 init();
126 return sharedLibs;
130 * Returns the paths of all shared libraries for the SDK.
132 public static List<File> getSharedLibFiles() {
133 init();
134 return sharedLibFiles;
138 * @deprecated Use {@link #getOptionalUserLibs()} instead.
140 @Deprecated
141 public static List<URL> getUserLibs() {
142 init();
143 return userLibs;
147 * @deprecated Use {@link #getOptionalUserLibs()} instead.
149 @Deprecated
150 public static List<File> getUserLibFiles() {
151 init();
152 return userLibFiles;
156 * Returns all optional user libraries for the SDK. Users who opt to use
157 * these libraries should both compile against and deploy them in the
158 * WEB-INF/lib folder of their web applications.
160 public static Collection<OptionalLib> getOptionalUserLibs() {
161 init();
162 return optionalUserLibsByName.values();
165 public static OptionalLib getOptionalUserLib(String name) {
166 init();
167 return optionalUserLibsByName.get(name);
171 * Returns all optional tools libraries for the SDK.
173 public static Collection<OptionalLib> getOptionalToolsLibs() {
174 init();
175 return optionalToolsLibsByName.values();
178 public static OptionalLib getOptionalToolsLib(String name) {
179 init();
180 return optionalToolsLibsByName.get(name);
184 * Returns the path to the root of the SDK.
186 public static File getSdkRoot() {
187 init();
188 return sdkRoot;
192 * Explicitly specifies the path to the root of the SDK. This takes
193 * precedence over the {@code appengine.sdk.root} system property,
194 * but must be called before any other methods in this class.
196 * @throws IllegalStateException If any other methods have already
197 * been called.
199 public synchronized static void setSdkRoot(File root) {
200 if (isInitialized && !sdkRoot.equals(root)) {
201 throw new IllegalStateException("Cannot set SDK root after initialization has occurred.");
203 sdkRoot = root;
206 public static Version getLocalVersion() {
207 return new LocalVersionFactory(getUserLibFiles()).getVersion();
210 public static String getDefaultServer() {
211 return DEFAULT_SERVER;
215 * If {@code true}, the testing jar will be added to the shared libs. This
216 * is intended for use by frameworks that want to run tests inside the
217 * isolated classloader.
219 * @param val Whether or the testing jar should be included on the shared
220 * path.
222 public static void includeTestingJarOnSharedPath(boolean val) {
223 isDevAppServerTest = val;
225 private synchronized static void init() {
226 if (!isInitialized) {
227 if (sdkRoot == null) {
228 sdkRoot = findSdkRoot();
230 sharedLibFiles = determineSharedLibFiles();
231 sharedLibs = Collections.unmodifiableList(toURLs(sharedLibFiles));
232 if (new File(sdkRoot, "lib" + File.separator + "user").isDirectory()) {
233 userLibFiles = Collections.unmodifiableList(getLibsRecursive(sdkRoot, "user"));
234 } else {
235 userLibFiles = Collections.emptyList();
237 userLibs = Collections.unmodifiableList(toURLs(userLibFiles));
238 optionalUserLibsByName = Collections.unmodifiableSortedMap(determineOptionalUserLibs());
239 optionalToolsLibsByName = Collections.unmodifiableSortedMap(determineOptionalToolsLibs());
240 isInitialized = true;
245 * Optional user libs reside under <sdk_root>/lib/opt/user. Each top-level
246 * directory under this path identifies an optional user library, and each
247 * sub-directory for a specific library represents a version of that library.
248 * So for example we could have:
249 * lib/opt/user/mylib1/v1/mylib.jar
250 * lib/opt/user/mylib1/v2/mylib.jar
251 * lib/opt/user/mylib2/v1/mylib.jar
252 * lib/opt/user/mylib2/v2/mylib.jar
254 * @return A {@link SortedMap} from the name of the library to an
255 * {@link OptionalLib} that describes the library. The map is sorted by
256 * library name.
258 private static SortedMap<String, OptionalLib> determineOptionalUserLibs() {
259 return determineOptionalLibs(new File(sdkRoot, "lib/opt/user"));
263 * Optional tools libs reside under <sdk_root>/lib/opt/tools. Each top-level
264 * directory under this path identifies an optional tools library, and each
265 * sub-directory for a specific library represents a version of that library.
266 * So for example we could have:
267 * lib/opt/tools/mylib1/v1/mylib.jar
268 * lib/opt/tools/mylib1/v2/mylib.jar
269 * lib/opt/tools/mylib2/v1/mylib.jar
270 * lib/opt/tools/mylib2/v2/mylib.jar
272 * @return A {@link SortedMap} from the name of the library to an
273 * {@link OptionalLib} that describes the library. The map is sorted by
274 * library name.
276 private static SortedMap<String, OptionalLib> determineOptionalToolsLibs() {
277 return determineOptionalLibs(new File(sdkRoot, "lib/opt/tools"));
280 private static SortedMap<String, OptionalLib> determineOptionalLibs(File root) {
281 SortedMap<String, OptionalLib> map = new TreeMap<String, OptionalLib>();
282 for (File libDir : listFiles(root)) {
283 SortedMap<String, List<File>> filesByVersion = new TreeMap<String, List<File>>();
284 for (File version : listFiles(libDir)) {
285 List<File> filesForVersion = new ArrayList<File>();
286 getLibs(version, filesForVersion, true);
287 filesByVersion.put(version.getName(), filesForVersion);
289 String description = "";
290 OptionalLib userLib =
291 new OptionalLib(libDir.getName(), description, filesByVersion);
292 map.put(userLib.getName(), userLib);
294 return map;
297 private static List<File> determineSharedLibFiles() {
298 List<File> sharedLibs = getLibsRecursive(sdkRoot, "shared");
299 if (isDevAppServerTest) {
300 sharedLibs.addAll(getLibsRecursive(sdkRoot, "testing"));
302 return Collections.unmodifiableList(sharedLibs);
306 * A version of {@link File#listFiles()} that never returns {@code null}.
307 * Historically this has been an issue, since listFiles() can return null if
308 * the parent directory does not exist or is not readable.
310 * @param dir The directory whose files we want to list.
311 * @return The contents of the provided directory.
313 static File[] listFiles(File dir) {
314 File[] files = dir.listFiles(NO_HIDDEN_FILES);
315 if (files == null) {
316 return new File[0];
318 return files;