Skip to content

Commit 319da7f

Browse files
Merge pull request #45 from Sanne/RuntimeUpdatesOptimizations
Parallelism and file content loading optimisations for RuntimeUpdates…
2 parents cd3ccb5 + f209da0 commit 319da7f

File tree

1 file changed

+72
-49
lines changed

1 file changed

+72
-49
lines changed

Diff for: maven/src/main/java/org/jboss/shamrock/maven/runner/RuntimeUpdatesHandler.java

+72-49
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
import java.lang.instrument.ClassDefinition;
1010
import java.nio.file.Files;
1111
import java.nio.file.Path;
12-
import java.util.HashMap;
13-
import java.util.HashSet;
12+
import java.util.Collections;
1413
import java.util.Map;
1514
import java.util.Set;
16-
import java.util.function.Consumer;
15+
import java.util.concurrent.ConcurrentMap;
16+
import java.util.concurrent.ConcurrentSkipListSet;
17+
import java.util.stream.Collectors;
18+
import java.util.stream.Stream;
1719

1820
import org.fakereplace.core.Fakereplace;
1921
import org.fakereplace.replacement.AddedClass;
@@ -86,57 +88,36 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
8688
}
8789

8890
private boolean doScan() throws IOException {
89-
Set<File> changedSourceFiles = new HashSet<>();
91+
final Set<File> changedSourceFiles;
9092
final long start = System.currentTimeMillis();
91-
if(sourcesDir != null) {
92-
Files.walk(sourcesDir).forEach(new Consumer<Path>() {
93-
@Override
94-
public void accept(Path path) {
95-
try {
96-
if (!path.toString().endsWith(".java")) {
97-
return;
98-
}
99-
long lastModified = Files.getLastModifiedTime(path).toMillis();
100-
if (lastModified > lastChange) {
101-
changedSourceFiles.add(path.toFile());
102-
}
103-
} catch (IOException e) {
104-
e.printStackTrace();
105-
}
106-
}
107-
});
93+
if (sourcesDir != null) {
94+
try (final Stream<Path> sourcesStream = Files.walk(sourcesDir)) {
95+
changedSourceFiles = sourcesStream
96+
.parallel()
97+
.filter(p -> p.toString().endsWith(".java"))
98+
.filter(p -> wasRecentlyModified(p))
99+
.map(Path::toFile)
100+
//Needing a concurrent Set, not many standard options:
101+
.collect(Collectors.toCollection(ConcurrentSkipListSet::new));
102+
}
103+
}
104+
else {
105+
changedSourceFiles = Collections.EMPTY_SET;
108106
}
109107
if (!changedSourceFiles.isEmpty()) {
110108
compiler.compile(changedSourceFiles);
111109
}
112-
Map<String, byte[]> changedClasses = new HashMap<>();
113-
Files.walk(classesDir).forEach(new Consumer<Path>() {
114-
@Override
115-
public void accept(Path path) {
116-
try {
117-
if (!path.toString().endsWith(".class")) {
118-
return;
119-
}
120-
long lastModified = Files.getLastModifiedTime(path).toMillis();
121-
if (lastModified > lastChange) {
122-
String pathName = classesDir.relativize(path).toString();
123-
String className = pathName.substring(0, pathName.length() - 6).replace("/", ".");
124-
ByteArrayOutputStream out = new ByteArrayOutputStream();
125-
byte[] buf = new byte[1024];
126-
int r;
127-
try (FileInputStream in = new FileInputStream(path.toFile())) {
128-
while ((r = in.read(buf)) > 0) {
129-
out.write(buf, 0, r);
130-
}
131-
changedClasses.put(className, out.toByteArray());
132-
}
133-
134-
}
135-
} catch (IOException e) {
136-
e.printStackTrace();
137-
}
138-
}
139-
});
110+
final ConcurrentMap<String, byte[]> changedClasses;
111+
try (final Stream<Path> classesStream = Files.walk(classesDir)) {
112+
changedClasses = classesStream
113+
.parallel()
114+
.filter(p -> p.toString().endsWith(".class"))
115+
.filter(p -> wasRecentlyModified(p))
116+
.collect(Collectors.toConcurrentMap(
117+
p -> pathToClassName(p),
118+
p -> readFileContentNoIOExceptions(p))
119+
);
120+
}
140121
if (changedClasses.isEmpty()) {
141122
return false;
142123
}
@@ -153,6 +134,48 @@ public void accept(Path path) {
153134
return true;
154135
}
155136

137+
private boolean wasRecentlyModified(final Path p) {
138+
try {
139+
return Files.getLastModifiedTime(p).toMillis() > lastChange;
140+
} catch (IOException e) {
141+
throw new RuntimeException(e);
142+
}
143+
}
144+
145+
private String pathToClassName(final Path path) {
146+
String pathName = classesDir.relativize(path).toString();
147+
String className = pathName.substring(0, pathName.length() - 6).replace("/", ".");
148+
return className;
149+
}
150+
151+
private byte[] readFileContentNoIOExceptions(final Path path) {
152+
try {
153+
return readFileContent(path);
154+
} catch (IOException e) {
155+
throw new RuntimeException(e);
156+
}
157+
}
158+
159+
private byte[] readFileContent(final Path path) throws IOException {
160+
final File file = path.toFile();
161+
final long fileLength = file.length();
162+
if (fileLength>Integer.MAX_VALUE) {
163+
throw new RuntimeException("Can't process class files larger than Integer.MAX_VALUE bytes");
164+
}
165+
try (FileInputStream stream = new FileInputStream(file)) {
166+
//Might be large but we need a single byte[] at the end of things, might as well allocate it in one shot:
167+
ByteArrayOutputStream out = new ByteArrayOutputStream((int)fileLength);
168+
byte[] buf = new byte[1024];
169+
int r;
170+
try (FileInputStream in = new FileInputStream(path.toFile())) {
171+
while ((r = in.read(buf)) > 0) {
172+
out.write(buf, 0, r);
173+
}
174+
return out.toByteArray();
175+
}
176+
}
177+
}
178+
156179
public static void displayErrorPage(HttpServerExchange exchange, final Throwable exception) throws IOException {
157180
StringBuilder sb = new StringBuilder();
158181
//todo: make this good

0 commit comments

Comments
 (0)