Skip to content

Commit 6a43273

Browse files
authored
Add jar file index to improve mc command compilation speed (alibaba#2736)
1 parent 61b0d9a commit 6a43273

File tree

4 files changed

+161
-36
lines changed

4 files changed

+161
-36
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.taobao.arthas.compiler;
2+
3+
/*-
4+
* #%L
5+
* compiler
6+
* %%
7+
* Copyright (C) 2017 - 2018 SkaLogs
8+
* %%
9+
* Licensed under the Apache License, Version 2.0 (the "License");
10+
* you may not use this file except in compliance with the License.
11+
* You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing, software
16+
* distributed under the License is distributed on an "AS IS" BASIS,
17+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* See the License for the specific language governing permissions and
19+
* limitations under the License.
20+
* #L%
21+
*/
22+
23+
import java.net.URI;
24+
25+
public class ClassUriWrapper {
26+
private final URI uri;
27+
28+
private final String className;
29+
30+
public ClassUriWrapper(String className, URI uri) {
31+
this.className = className;
32+
this.uri = uri;
33+
}
34+
35+
public URI getUri() {
36+
return uri;
37+
}
38+
39+
public String getClassName() {
40+
return className;
41+
}
42+
}

memorycompiler/src/main/java/com/taobao/arthas/compiler/CustomJavaFileObject.java

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,20 @@
2323
import javax.lang.model.element.Modifier;
2424
import javax.lang.model.element.NestingKind;
2525
import javax.tools.JavaFileObject;
26-
import java.io.*;
26+
import java.io.IOException;
27+
import java.io.InputStream;
28+
import java.io.OutputStream;
29+
import java.io.Reader;
30+
import java.io.Writer;
2731
import java.net.URI;
2832

2933
public class CustomJavaFileObject implements JavaFileObject {
30-
private final String binaryName;
34+
private final String className;
3135
private final URI uri;
32-
private final String name;
3336

34-
public CustomJavaFileObject(String binaryName, URI uri) {
37+
public CustomJavaFileObject(String className, URI uri) {
3538
this.uri = uri;
36-
this.binaryName = binaryName;
37-
name = uri.getPath() == null ? uri.getSchemeSpecificPart() : uri.getPath(); // for FS based URI the path is not null, for JAR URI the scheme specific part is not null
39+
this.className = className;
3840
}
3941

4042
public URI toUri() {
@@ -50,7 +52,7 @@ public OutputStream openOutputStream() {
5052
}
5153

5254
public String getName() {
53-
return name;
55+
return this.className;
5456
}
5557

5658
public Reader openReader(boolean ignoreEncodingErrors) {
@@ -78,10 +80,8 @@ public Kind getKind() {
7880
}
7981

8082
public boolean isNameCompatible(String simpleName, Kind kind) {
81-
String baseName = simpleName + kind.extension;
82-
return kind.equals(getKind())
83-
&& (baseName.equals(getName())
84-
|| getName().endsWith("/" + baseName));
83+
return Kind.CLASS.equals(getKind())
84+
&& this.className.endsWith(simpleName);
8585
}
8686

8787
public NestingKind getNestingKind() {
@@ -92,8 +92,8 @@ public Modifier getAccessLevel() {
9292
throw new UnsupportedOperationException();
9393
}
9494

95-
public String binaryName() {
96-
return binaryName;
95+
public String getClassName() {
96+
return this.className;
9797
}
9898

9999

memorycompiler/src/main/java/com/taobao/arthas/compiler/DynamicJavaFileManager.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ public class DynamicJavaFileManager extends ForwardingJavaFileManager<JavaFileMa
2424
public DynamicJavaFileManager(JavaFileManager fileManager, DynamicClassLoader classLoader) {
2525
super(fileManager);
2626
this.classLoader = classLoader;
27-
28-
finder = new PackageInternalsFinder(classLoader);
27+
this.finder = new PackageInternalsFinder(classLoader);
2928
}
3029

3130
@Override
@@ -53,7 +52,7 @@ public ClassLoader getClassLoader(JavaFileManager.Location location) {
5352
@Override
5453
public String inferBinaryName(Location location, JavaFileObject file) {
5554
if (file instanceof CustomJavaFileObject) {
56-
return ((CustomJavaFileObject) file).binaryName();
55+
return ((CustomJavaFileObject) file).getClassName();
5756
} else {
5857
/**
5958
* if it's not CustomJavaFileObject, then it's coming from standard file manager

memorycompiler/src/main/java/com/taobao/arthas/compiler/PackageInternalsFinder.java

Lines changed: 104 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,23 @@
2828
import java.net.URL;
2929
import java.net.URLDecoder;
3030
import java.util.ArrayList;
31+
import java.util.Arrays;
3132
import java.util.Collection;
33+
import java.util.Collections;
3234
import java.util.Enumeration;
35+
import java.util.HashMap;
3336
import java.util.List;
37+
import java.util.Map;
38+
import java.util.concurrent.ConcurrentHashMap;
3439
import java.util.jar.JarEntry;
40+
import java.util.stream.Collectors;
3541

3642
public class PackageInternalsFinder {
3743
private final ClassLoader classLoader;
3844
private static final String CLASS_FILE_EXTENSION = ".class";
3945

46+
private static final Map<String, JarFileIndex> INDEXS = new ConcurrentHashMap<>();
47+
4048
public PackageInternalsFinder(ClassLoader classLoader) {
4149
this.classLoader = classLoader;
4250
}
@@ -60,11 +68,30 @@ private Collection<JavaFileObject> listUnder(String packageName, URL packageFold
6068
if (directory.isDirectory()) { // browse local .class files - useful for local execution
6169
return processDir(packageName, directory);
6270
} else { // browse a jar file
63-
return processJar(packageFolderURL);
64-
} // maybe there can be something else for more involved class loaders
71+
return processJar(packageName, packageFolderURL);
72+
}
73+
}
74+
75+
private List<JavaFileObject> processJar(String packageName, URL packageFolderURL) {
76+
try {
77+
String jarUri = packageFolderURL.toExternalForm().substring(0, packageFolderURL.toExternalForm().lastIndexOf("!/"));
78+
JarFileIndex jarFileIndex = INDEXS.get(jarUri);
79+
if (jarFileIndex == null) {
80+
jarFileIndex = new JarFileIndex(jarUri, URI.create(jarUri + "!/"));
81+
INDEXS.put(jarUri, jarFileIndex);
82+
}
83+
List<JavaFileObject> result = jarFileIndex.search(packageName);
84+
if (result != null) {
85+
return result;
86+
}
87+
} catch (Exception e) {
88+
// ignore
89+
}
90+
// 保底
91+
return fuse(packageFolderURL);
6592
}
6693

67-
private List<JavaFileObject> processJar(URL packageFolderURL) {
94+
private List<JavaFileObject> fuse(URL packageFolderURL) {
6895
List<JavaFileObject> result = new ArrayList<JavaFileObject>();
6996
try {
7097
String jarUri = packageFolderURL.toExternalForm().substring(0, packageFolderURL.toExternalForm().lastIndexOf("!/"));
@@ -92,24 +119,16 @@ private List<JavaFileObject> processJar(URL packageFolderURL) {
92119
}
93120

94121
private List<JavaFileObject> processDir(String packageName, File directory) {
95-
List<JavaFileObject> result = new ArrayList<JavaFileObject>();
96-
97-
File[] childFiles = directory.listFiles();
98-
if (childFiles != null) {
99-
for (File childFile : childFiles) {
100-
if (childFile.isFile()) {
101-
// We only want the .class files.
102-
if (childFile.getName().endsWith(CLASS_FILE_EXTENSION)) {
103-
String binaryName = packageName + "." + childFile.getName();
104-
binaryName = binaryName.replaceAll(CLASS_FILE_EXTENSION + "$", "");
105-
106-
result.add(new CustomJavaFileObject(binaryName, childFile.toURI()));
107-
}
108-
}
109-
}
122+
File[] files = directory.listFiles(item ->
123+
item.isFile() && getKind(item.getName()) == JavaFileObject.Kind.CLASS);
124+
if (files != null) {
125+
return Arrays.stream(files).map(item -> {
126+
String className = packageName + "." + item.getName()
127+
.replaceAll(CLASS_FILE_EXTENSION + "$", "");
128+
return new CustomJavaFileObject(className, item.toURI());
129+
}).collect(Collectors.toList());
110130
}
111-
112-
return result;
131+
return Collections.emptyList();
113132
}
114133

115134
private String decode(String filePath) {
@@ -121,4 +140,69 @@ private String decode(String filePath) {
121140

122141
return filePath;
123142
}
143+
144+
public static JavaFileObject.Kind getKind(String name) {
145+
if (name.endsWith(JavaFileObject.Kind.CLASS.extension))
146+
return JavaFileObject.Kind.CLASS;
147+
else if (name.endsWith(JavaFileObject.Kind.SOURCE.extension))
148+
return JavaFileObject.Kind.SOURCE;
149+
else if (name.endsWith(JavaFileObject.Kind.HTML.extension))
150+
return JavaFileObject.Kind.HTML;
151+
else
152+
return JavaFileObject.Kind.OTHER;
153+
}
154+
155+
public static class JarFileIndex {
156+
private String jarUri;
157+
private URI uri;
158+
159+
private Map<String, List<ClassUriWrapper>> packages = new HashMap<>();
160+
161+
public JarFileIndex(String jarUri, URI uri) throws IOException {
162+
this.jarUri = jarUri;
163+
this.uri = uri;
164+
loadIndex();
165+
}
166+
167+
private void loadIndex() throws IOException {
168+
JarURLConnection jarConn = (JarURLConnection) uri.toURL().openConnection();
169+
String rootEntryName = jarConn.getEntryName() == null ? "" : jarConn.getEntryName();
170+
Enumeration<JarEntry> entryEnum = jarConn.getJarFile().entries();
171+
while (entryEnum.hasMoreElements()) {
172+
JarEntry jarEntry = entryEnum.nextElement();
173+
String entryName = jarEntry.getName();
174+
if (entryName.startsWith(rootEntryName) && entryName.endsWith(CLASS_FILE_EXTENSION)) {
175+
String className = entryName
176+
.substring(0, entryName.length() - CLASS_FILE_EXTENSION.length())
177+
.replace(rootEntryName, "")
178+
.replace("/", ".");
179+
if (className.startsWith(".")) className = className.substring(1);
180+
if (className.equals("package-info")
181+
|| className.equals("module-info")
182+
|| className.lastIndexOf(".") == -1) {
183+
continue;
184+
}
185+
String packageName = className.substring(0, className.lastIndexOf("."));
186+
List<ClassUriWrapper> classes = packages.get(packageName);
187+
if (classes == null) {
188+
classes = new ArrayList<>();
189+
packages.put(packageName, classes);
190+
}
191+
classes.add(new ClassUriWrapper(className, URI.create(jarUri + "!/" + entryName)));
192+
}
193+
}
194+
}
195+
196+
public List<JavaFileObject> search(String packageName) {
197+
if (this.packages.isEmpty()) {
198+
return null;
199+
}
200+
if (this.packages.containsKey(packageName)) {
201+
return packages.get(packageName).stream().map(item -> {
202+
return new CustomJavaFileObject(item.getClassName(), item.getUri());
203+
}).collect(Collectors.toList());
204+
}
205+
return Collections.emptyList();
206+
}
207+
}
124208
}

0 commit comments

Comments
 (0)