Skip to content

Commit 123ea8e

Browse files
authored
Merge pull request #44128 from ia3andy/fix-web-dep-locator
Safer lookup and generate static resources for web dep locator
2 parents fcc7889 + 693cdca commit 123ea8e

File tree

3 files changed

+99
-95
lines changed

3 files changed

+99
-95
lines changed

docs/src/main/asciidoc/web-dependency-locator.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ This means adding the following to your `index.html` will allow you to import we
113113

114114
===== Automatic imports
115115

116-
You can also automate the imports above. To do this, move your web assets from `src/main/resources/META-INF/resources` to `src/main/web`
116+
You can also automate the imports above. To do this, move your web assets from `src/main/resources/META-INF/resources` to `src/main/resources/web`
117117
and now replace the above scripts and imports with `{#bundle /}`:
118118

119119
[source,html]

extensions/web-dependency-locator/deployment/src/main/java/io/quarkus/webdependency/locator/deployment/WebDependencyLocatorProcessor.java

Lines changed: 63 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import java.net.URL;
88
import java.nio.file.Files;
99
import java.nio.file.Path;
10-
import java.nio.file.StandardOpenOption;
1110
import java.util.ArrayList;
1211
import java.util.HashMap;
1312
import java.util.HashSet;
@@ -30,10 +29,10 @@
3029
import io.quarkus.deployment.builditem.FeatureBuildItem;
3130
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
3231
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
33-
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
3432
import io.quarkus.maven.dependency.ArtifactKey;
3533
import io.quarkus.maven.dependency.ResolvedDependency;
3634
import io.quarkus.vertx.http.deployment.RouteBuildItem;
35+
import io.quarkus.vertx.http.deployment.spi.GeneratedStaticResourceBuildItem;
3736
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig;
3837
import io.quarkus.webdependency.locator.runtime.WebDependencyLocatorRecorder;
3938
import io.vertx.core.Handler;
@@ -43,96 +42,81 @@ public class WebDependencyLocatorProcessor {
4342
private static final Logger log = Logger.getLogger(WebDependencyLocatorProcessor.class.getName());
4443

4544
@BuildStep
46-
public void findRelevantFiles(BuildProducer<FeatureBuildItem> feature,
47-
BuildProducer<HotDeploymentWatchedFileBuildItem> hotDeploymentWatchedProducer,
48-
WebDependencyLocatorConfig config,
49-
OutputTargetBuildItem outputTarget) throws IOException {
50-
51-
Path web = outputTarget.getOutputDirectory().getParent()
52-
.resolve(SRC)
53-
.resolve(MAIN)
54-
.resolve(RESOURCES)
55-
.resolve(config.webRoot);
56-
57-
if (Files.exists(web)) {
58-
hotDeploymentWatchedProducer.produce(new HotDeploymentWatchedFileBuildItem(config.webRoot + SLASH + STAR + STAR));
59-
// Find all css and js (under /app)
60-
Path app = web
61-
.resolve(config.appRoot);
45+
public void feature(BuildProducer<FeatureBuildItem> feature) {
46+
feature.produce(new FeatureBuildItem(Feature.WEB_DEPENDENCY_LOCATOR));
47+
}
6248

63-
List<Path> cssFiles = new ArrayList<>();
64-
List<Path> jsFiles = new ArrayList<>();
49+
@BuildStep
50+
public void findRelevantFiles(BuildProducer<GeneratedStaticResourceBuildItem> generatedStaticProducer,
51+
BuildProducer<HotDeploymentWatchedFileBuildItem> hotDeploymentWatchedProducer,
52+
WebDependencyLocatorConfig config) throws IOException {
6553

66-
if (Files.exists(app)) {
54+
QuarkusClassLoader.visitRuntimeResources(config.webRoot, visit -> {
55+
final Path web = visit.getPath();
56+
if (Files.isDirectory(web)) {
6757
hotDeploymentWatchedProducer
68-
.produce(new HotDeploymentWatchedFileBuildItem(
69-
config.webRoot + SLASH + config.appRoot + SLASH + STAR + STAR));
70-
try (Stream<Path> appstream = Files.walk(app)) {
71-
appstream.forEach(path -> {
72-
if (Files.isRegularFile(path) && path.toString().endsWith(DOT_CSS)) {
73-
cssFiles.add(web.relativize(path));
74-
} else if (Files.isRegularFile(path) && path.toString().endsWith(DOT_JS)) {
75-
jsFiles.add(web.relativize(path));
58+
.produce(new HotDeploymentWatchedFileBuildItem(config.webRoot + SLASH + STAR + STAR));
59+
// Find all css and js (under /app)
60+
Path app = web
61+
.resolve(config.appRoot);
62+
63+
List<Path> cssFiles = new ArrayList<>();
64+
List<Path> jsFiles = new ArrayList<>();
65+
66+
if (Files.exists(app)) {
67+
hotDeploymentWatchedProducer
68+
.produce(new HotDeploymentWatchedFileBuildItem(
69+
config.webRoot + SLASH + config.appRoot + SLASH + STAR + STAR));
70+
try (Stream<Path> appstream = Files.walk(app)) {
71+
appstream.forEach(path -> {
72+
if (Files.isRegularFile(path) && path.toString().endsWith(DOT_CSS)) {
73+
cssFiles.add(web.relativize(path));
74+
} else if (Files.isRegularFile(path) && path.toString().endsWith(DOT_JS)) {
75+
jsFiles.add(web.relativize(path));
76+
}
77+
});
78+
} catch (IOException e) {
79+
throw new RuntimeException(e);
80+
}
81+
}
82+
83+
try (Stream<Path> webstream = Files.walk(web)) {
84+
85+
webstream.forEach(path -> {
86+
if (Files.isRegularFile(path)) {
87+
String endpoint = SLASH + web.relativize(path);
88+
try {
89+
if (path.toString().endsWith(DOT_HTML)) {
90+
generatedStaticProducer.produce(new GeneratedStaticResourceBuildItem(endpoint,
91+
processHtml(path, cssFiles, jsFiles)));
92+
} else {
93+
generatedStaticProducer.produce(new GeneratedStaticResourceBuildItem(endpoint, path));
94+
}
95+
} catch (IOException e) {
96+
throw new UncheckedIOException(e);
97+
}
7698
}
7799
});
100+
} catch (IOException e) {
101+
throw new UncheckedIOException(e);
78102
}
79103
}
104+
});
80105

81-
try (Stream<Path> webstream = Files.walk(web)) {
82-
83-
final Path resourcesDirectory = outputTarget.getOutputDirectory()
84-
.resolve(CLASSES)
85-
.resolve(META_INF)
86-
.resolve(RESOURCES);
87-
Files.createDirectories(resourcesDirectory);
88-
89-
webstream.forEach(path -> {
90-
if (Files.isRegularFile(path)) {
91-
try {
92-
copyResource(resourcesDirectory, web, path, cssFiles, jsFiles, path.toString().endsWith(DOT_HTML));
93-
} catch (IOException e) {
94-
throw new UncheckedIOException(e);
95-
}
96-
} else if (Files.isRegularFile(path)) {
97-
98-
}
99-
});
100-
} catch (IOException e) {
101-
throw new UncheckedIOException(e);
102-
}
103-
}
104-
feature.produce(new FeatureBuildItem(Feature.WEB_DEPENDENCY_LOCATOR));
105106
}
106107

107-
private void copyResource(Path resourcesDirectory, Path webRoot, Path path, List<Path> cssFiles, List<Path> jsFiles,
108-
boolean filter)
108+
private byte[] processHtml(
109+
Path path, List<Path> cssFiles, List<Path> jsFiles)
109110
throws IOException {
110-
try {
111+
StringJoiner modifiedContent = new StringJoiner(System.lineSeparator());
111112

112-
Path relativizePath = webRoot.relativize(path);
113+
Files.lines(path).forEach(line -> {
114+
String modifiedLine = processLine(line, cssFiles, jsFiles);
115+
modifiedContent.add(modifiedLine);
116+
});
113117

114-
byte[] toBeCopied;
115-
if (filter) {
116-
StringJoiner modifiedContent = new StringJoiner(System.lineSeparator());
117-
118-
Files.lines(path).forEach(line -> {
119-
String modifiedLine = processLine(line, cssFiles, jsFiles);
120-
modifiedContent.add(modifiedLine);
121-
});
122-
123-
String result = modifiedContent.toString();
124-
toBeCopied = result.getBytes();
125-
} else {
126-
toBeCopied = Files.readAllBytes(path);
127-
}
128-
129-
final Path resourceFile = resourcesDirectory.resolve(relativizePath);
130-
Files.createDirectories(resourceFile.getParent());
131-
Files.write(resourceFile, toBeCopied, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
132-
133-
} catch (IOException e) {
134-
throw new UncheckedIOException(e);
135-
}
118+
String result = modifiedContent.toString();
119+
return result.getBytes();
136120
}
137121

138122
private static String processLine(String line, List<Path> cssFiles, List<Path> jsFiles) {
@@ -310,13 +294,6 @@ static class LibInfo {
310294
private static final String TAB = "\t";
311295
private static final String TAB2 = TAB + TAB;
312296

313-
private static final String CLASSES = "classes";
314-
private static final String META_INF = "META-INF";
315-
private static final String RESOURCES = "resources";
316-
317-
private static final String SRC = "src";
318-
private static final String MAIN = "main";
319-
320297
private static final String SLASH = "/";
321298
private static final String STAR = "*";
322299

independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
import java.util.Set;
2424
import java.util.concurrent.ConcurrentHashMap;
2525
import java.util.concurrent.ConcurrentMap;
26+
import java.util.function.Consumer;
2627

2728
import org.jboss.logging.Logger;
2829

2930
import io.quarkus.commons.classloading.ClassLoaderHelper;
3031
import io.quarkus.paths.ManifestAttributes;
32+
import io.quarkus.paths.PathVisit;
3133

3234
/**
3335
* The ClassLoader used for non production Quarkus applications (i.e. dev and test mode).
@@ -48,14 +50,42 @@ public class QuarkusClassLoader extends ClassLoader implements Closeable {
4850
registerAsParallelCapable();
4951
}
5052

53+
private static RuntimeException nonQuarkusClassLoaderError() {
54+
return new IllegalStateException("The current classloader is not an instance of "
55+
+ QuarkusClassLoader.class.getName() + " but "
56+
+ Thread.currentThread().getContextClassLoader().getClass().getName());
57+
}
58+
59+
/**
60+
* Visits every found runtime resource with a given name. If a resource is not found, the visitor will
61+
* simply not be called.
62+
* <p>
63+
* IMPORTANT: this method works only when the current class loader is an instance of {@link QuarkusClassLoader},
64+
* otherwise it throws an error with the corresponding message.
65+
*
66+
* @param resourceName runtime resource name to visit
67+
* @param visitor runtime resource visitor
68+
*/
69+
public static void visitRuntimeResources(String resourceName, Consumer<PathVisit> visitor) {
70+
if (Thread.currentThread().getContextClassLoader() instanceof QuarkusClassLoader classLoader) {
71+
for (var element : classLoader.getElementsWithResource(resourceName)) {
72+
if (element.isRuntime()) {
73+
element.apply(tree -> {
74+
tree.accept(resourceName, visitor);
75+
return null;
76+
});
77+
}
78+
}
79+
} else {
80+
throw nonQuarkusClassLoaderError();
81+
}
82+
}
83+
5184
public static List<ClassPathElement> getElements(String resourceName, boolean onlyFromCurrentClassLoader) {
5285
if (Thread.currentThread().getContextClassLoader() instanceof QuarkusClassLoader classLoader) {
5386
return classLoader.getElementsWithResource(resourceName, onlyFromCurrentClassLoader);
5487
}
55-
56-
throw new IllegalStateException("The current classloader is not an instance of "
57-
+ QuarkusClassLoader.class.getName() + " but "
58-
+ Thread.currentThread().getContextClassLoader().getClass().getName());
88+
throw nonQuarkusClassLoaderError();
5989
}
6090

6191
/**
@@ -78,10 +108,7 @@ public static boolean isApplicationClass(String className) {
78108

79109
return classPathResourceIndex.getFirstClassPathElement(resourceName) != null;
80110
}
81-
82-
throw new IllegalStateException("The current classloader is not an instance of "
83-
+ QuarkusClassLoader.class.getName() + " but "
84-
+ Thread.currentThread().getContextClassLoader().getClass().getName());
111+
throw nonQuarkusClassLoaderError();
85112
}
86113

87114
/**

0 commit comments

Comments
 (0)