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
;
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
;
15 import java
.util
.regex
.Pattern
;
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
{
28 final double priority
;
30 UrlPriority(URL url
, double priority
) {
32 this.priority
= priority
;
36 public int hashCode() {
39 result
= prime
* result
+ ((url
== null) ?
0 : url
.hashCode());
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
;
50 if (other
.url
!= null) return false;
51 } else if (!url
.equals(other
.url
)) return false;
56 private static final Comparator
<UrlPriority
> URL_PRIORITY_COMP
= new Comparator
<UrlPriority
>() {
58 public int compare(UrlPriority o1
, UrlPriority o2
) {
59 double diff
= o1
.priority
- o2
.priority
;
70 private Set
<UrlPriority
> urls
= new LinkedHashSet
<UrlPriority
>();
72 private final List
<PrioritySpecifierEntry
> priorityEntries
;
74 private final boolean[] usedPrioritySpecifiers
;
76 private URL
[] sortedUrls
= null;
79 * @param classLoaderConfig The class loader config, may be null.
81 public ClassPathBuilder(ClassLoaderConfig classLoaderConfig
) {
82 if (classLoaderConfig
== null) {
83 priorityEntries
= ImmutableList
.of();
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();
111 * Add the classes {@link URL URLs}.
113 public void addClassesUrl(URL url
) {
118 * Add the appengine-api.jar {@link URL URLs}.
120 public void addAppengineJar(URL url
) {
125 * Add application specific jar {@link URL URLs}.
127 public void addAppJar(URL url
) {
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
;
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()) {
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.";
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()) {
187 } else if (APPENGINE_API_REGEX
.matcher(url
.getPath()).matches()) {
188 addAppengineJar(url
);