App Engine 1.8.4.
[gae.git] / java / src / main / com / google / apphosting / utils / config / ClassPathBuilder.java
blob1d2cbe70d0faef533069b16f5f09c10b9ddad692
1 package com.google.apphosting.utils.config;
3 import com.google.apphosting.utils.config.AppEngineWebXml.ClassLoaderConfig;
4 import com.google.apphosting.utils.config.AppEngineWebXml.PrioritySpecifierEntry;
5 import com.google.common.collect.ImmutableList;
7 import java.io.File;
8 import java.net.URL;
9 import java.util.Arrays;
10 import java.util.Collection;
11 import java.util.Comparator;
12 import java.util.LinkedHashSet;
13 import java.util.List;
14 import java.util.Set;
15 import java.util.regex.Pattern;
17 /**
18 * Applies class loader configuration rules to class path {@link URL URLs}.
20 public class ClassPathBuilder {
21 private static final Pattern CLASSES_REGEX = Pattern.compile(".*/classes/?");
23 private static final Pattern APPENGINE_API_REGEX =
24 Pattern.compile(".*/appengine-api(-?[0-9\\.]*-sdk-[0-9\\.]*)?\\.jar");
26 private static class UrlPriority {
27 final URL url;
28 final double priority;
30 UrlPriority(URL url, double priority) {
31 this.url = url;
32 this.priority = priority;
35 @Override
36 public int hashCode() {
37 final int prime = 31;
38 int result = 1;
39 result = prime * result + ((url == null) ? 0 : url.hashCode());
40 return result;
43 @Override
44 public boolean equals(Object obj) {
45 if (this == obj) return true;
46 if (obj == null) return false;
47 if (getClass() != obj.getClass()) return false;
48 UrlPriority other = (UrlPriority) obj;
49 if (url == null) {
50 if (other.url != null) return false;
51 } else if (!url.equals(other.url)) return false;
52 return true;
56 private static final Comparator<UrlPriority> URL_PRIORITY_COMP = new Comparator<UrlPriority>() {
57 @Override
58 public int compare(UrlPriority o1, UrlPriority o2) {
59 double diff = o1.priority - o2.priority;
60 if (diff < 0) {
61 return 1;
63 if (diff > 0) {
64 return -1;
66 return 0;
70 private Set<UrlPriority> urls = new LinkedHashSet<UrlPriority>();
72 private final List<PrioritySpecifierEntry> priorityEntries;
74 private final boolean[] usedPrioritySpecifiers;
76 private URL[] sortedUrls = null;
78 /**
79 * @param classLoaderConfig The class loader config, may be null.
81 public ClassPathBuilder(ClassLoaderConfig classLoaderConfig) {
82 if (classLoaderConfig == null) {
83 priorityEntries = ImmutableList.of();
84 } else {
85 priorityEntries = classLoaderConfig.getEntries();
87 usedPrioritySpecifiers = new boolean[priorityEntries.size()];
90 private void addUrl(URL url, double defaultPriority) {
91 if (sortedUrls != null) {
92 throw new IllegalStateException("add* calls are not allowed after getUrls() has been called");
94 Double priority = findPriority(url);
95 urls.add(new UrlPriority(url, null == priority ? defaultPriority : priority));
98 private Double findPriority(URL url) {
99 String fileName = new File(url.getPath()).getName();
101 for (int i = 0; i < usedPrioritySpecifiers.length; ++i) {
102 if (priorityEntries.get(i).getFilename().equals(fileName)) {
103 usedPrioritySpecifiers[i] = true;
104 return priorityEntries.get(i).getPriorityValue();
107 return null;
111 * Add the classes {@link URL URLs}.
113 public void addClassesUrl(URL url) {
114 addUrl(url, 100.0d);
118 * Add the appengine-api.jar {@link URL URLs}.
120 public void addAppengineJar(URL url) {
121 addUrl(url, 0.5d);
125 * Add application specific jar {@link URL URLs}.
127 public void addAppJar(URL url) {
128 addUrl(url, 0.0d);
132 * Returns the class loader urls in the order modified by the priority
133 * specifiers in the {@link ClassLoaderConfig} passed to the constructor.
135 public URL[] getUrls() {
136 if (sortedUrls == null) {
137 UrlPriority[] classPath = urls.toArray(new UrlPriority[urls.size()]);
138 Arrays.sort(classPath, URL_PRIORITY_COMP);
140 sortedUrls = new URL[classPath.length];
141 for (int i = 0; i < classPath.length; ++i) {
142 sortedUrls[i] = classPath[i].url;
145 return sortedUrls;
149 * Returns a log message if there were unused specifiers or an empty string.
150 * <p>Must be called after calling {@link #getUrls()}.
152 public String getLogMessage() {
153 if (sortedUrls == null) {
154 throw new IllegalStateException(
155 "Cannot call getLogMessage() without first calling getUrls()");
157 StringBuilder builder = new StringBuilder();
158 for (int i = 0; i < usedPrioritySpecifiers.length; ++i) {
159 if (!usedPrioritySpecifiers[i]) {
160 builder.append("priority-specifier: filename: ");
161 builder.append(priorityEntries.get(i).getFilename());
162 if (priorityEntries.get(i).getPriority() != null) {
163 builder.append(" priority: ");
164 builder.append(priorityEntries.get(i).getPriorityValue());
168 String errors = builder.toString();
169 if (!errors.isEmpty()) {
170 return
171 "appengine-web.xml contains unused class-loader-config priority-specifier values.\n" +
172 "unused values: " + errors + "\nresulting classpath: " + Arrays.toString(sortedUrls) +
173 "\nTo remove this warning, remove the unused priority-specifier values " +
174 "from appengine-web.xml or add a file matching the unused priority-specifier values.";
176 return "";
180 * Scans through a collection of URLs for various patterns and adds them
181 * with the correct priority.
183 public void addUrls(Collection<URL> urls) {
184 for (URL url : urls) {
185 if (CLASSES_REGEX.matcher(url.getPath()).matches()) {
186 addClassesUrl(url);
187 } else if (APPENGINE_API_REGEX.matcher(url.getPath()).matches()) {
188 addAppengineJar(url);
189 } else {
190 addAppJar(url);