Skip to content

Commit

Permalink
Merge pull request #21162 from mkouba/issue-18525
Browse files Browse the repository at this point in the history
Qute - scan "templates" directory in all application archives
  • Loading branch information
gastaldi authored Nov 3, 2021
2 parents c0b36d9 + 61324b2 commit 442f14e
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -129,6 +130,7 @@ public class QuteProcessor {

private static final String CHECKED_TEMPLATE_REQUIRE_TYPE_SAFE = "requireTypeSafeExpressions";
private static final String CHECKED_TEMPLATE_BASE_PATH = "basePath";
private static final String BASE_PATH = "templates";

private static final Function<FieldInfo, String> GETTER_FUN = new Function<FieldInfo, String>() {
@Override
Expand Down Expand Up @@ -331,7 +333,10 @@ List<CheckedTemplateBuildItem> collectCheckedTemplates(BeanArchiveIndexBuildItem
TemplatesAnalysisBuildItem analyzeTemplates(List<TemplatePathBuildItem> templatePaths,
TemplateFilePathsBuildItem filePaths, List<CheckedTemplateBuildItem> checkedTemplates,
List<MessageBundleMethodBuildItem> messageBundleMethods, QuteConfig config) {
long start = System.currentTimeMillis();
long start = System.nanoTime();

checkDuplicatePaths(templatePaths);

List<TemplateAnalysis> analysis = new ArrayList<>();

// A dummy engine instance is used to parse and validate all templates during the build
Expand Down Expand Up @@ -462,7 +467,8 @@ public void beforeParsing(ParserHelper parserHelper) {
+ "()"));
}

LOGGER.debugf("Finished analysis of %s templates in %s ms", analysis.size(), System.currentTimeMillis() - start);
LOGGER.debugf("Finished analysis of %s templates in %s ms", analysis.size(),
TimeUnit.NANOSECONDS.toMillis(start - System.nanoTime()));
return new TemplatesAnalysisBuildItem(analysis);
}

Expand Down Expand Up @@ -1177,27 +1183,28 @@ public Function<FieldInfo, String> apply(ClassInfo clazz) {
}

@BuildStep
void collectTemplates(ApplicationArchivesBuildItem applicationArchivesBuildItem,
void collectTemplates(ApplicationArchivesBuildItem applicationArchives,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedPaths,
BuildProducer<TemplatePathBuildItem> templatePaths,
BuildProducer<NativeImageResourceBuildItem> nativeImageResources,
QuteConfig config)
throws IOException {
ApplicationArchive applicationArchive = applicationArchivesBuildItem.getRootArchive();
String basePath = "templates";
Path templatesPath = null;
for (Path rootDir : applicationArchive.getRootDirectories()) {
// Note that we cannot use ApplicationArchive.getChildPath(String) here because we would not be able to detect
// a wrong directory name on case-insensitive file systems
templatesPath = Files.list(rootDir).filter(p -> p.getFileName().toString().equals(basePath)).findFirst()
.orElse(null);
if (templatesPath != null) {
break;
Set<Path> basePaths = new HashSet<>();

for (ApplicationArchive archive : applicationArchives.getAllApplicationArchives()) {
for (Path rootDir : archive.getRootDirectories()) {
// Note that we cannot use ApplicationArchive.getChildPath(String) here because we would not be able to detect
// a wrong directory name on case-insensitive file systems
Path basePath = Files.list(rootDir).filter(QuteProcessor::isBasePath).findFirst().orElse(null);
if (basePath != null) {
LOGGER.debugf("Found templates dir: %s", basePath);
basePaths.add(basePath);
break;
}
}
}
if (templatesPath != null) {
LOGGER.debugf("Found templates dir: %s", templatesPath);
scan(templatesPath, templatesPath, basePath + "/", watchedPaths, templatePaths, nativeImageResources,
for (Path base : basePaths) {
scan(base, base, BASE_PATH + "/", watchedPaths, templatePaths, nativeImageResources,
config.templatePathExclude);
}
}
Expand Down Expand Up @@ -2095,6 +2102,29 @@ private static boolean isExcluded(TypeCheck check, List<TypeCheckExcludeBuildIte
return false;
}

private static boolean isBasePath(Path path) {
return path.getFileName().toString().equals(BASE_PATH);
}

private void checkDuplicatePaths(List<TemplatePathBuildItem> templatePaths) {
Map<String, List<TemplatePathBuildItem>> duplicates = templatePaths.stream()
.collect(Collectors.groupingBy(TemplatePathBuildItem::getPath));
for (Iterator<List<TemplatePathBuildItem>> it = duplicates.values().iterator(); it.hasNext();) {
List<TemplatePathBuildItem> paths = it.next();
if (paths.isEmpty() || paths.size() == 1) {
it.remove();
}
}
if (!duplicates.isEmpty()) {
StringBuilder builder = new StringBuilder("Duplicate templates found:");
for (Entry<String, List<TemplatePathBuildItem>> e : duplicates.entrySet()) {
builder.append("\n\t- ").append(e.getKey()).append(": ")
.append(e.getValue().stream().map(TemplatePathBuildItem::getFullPath).collect(Collectors.toList()));
}
throw new IllegalStateException(builder.toString());
}
}

/**
* Java members lookup config.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.quarkus.qute.deployment.scanning;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

public class MultipleTemplatesDirectoryDuplicateFoundTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root.addAsResource(new StringAsset("Hello!"), "templates/hello.html"))
.withAdditionalDependency(
d -> d.addAsResource(new StringAsset("Hi!"), "templates/hello.html"))
.assertException(t -> {
Throwable e = t;
IllegalStateException ise = null;
while (e != null) {
if (e instanceof IllegalStateException) {
ise = (IllegalStateException) e;
break;
}
e = e.getCause();
}
assertNotNull(ise);
assertTrue(ise.getMessage().contains("Duplicate templates found:"), ise.getMessage());
assertTrue(ise.getMessage().contains("hello.html"), ise.getMessage());
});

@Test
public void testValidation() {
fail();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.quarkus.qute.deployment.scanning;

import static org.junit.jupiter.api.Assertions.assertEquals;

import javax.inject.Inject;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.qute.Template;
import io.quarkus.test.QuarkusUnitTest;

public class MultipleTemplatesDirectoryTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root.addAsResource(new StringAsset("Hello!"), "templates/hello.html"))
.withAdditionalDependency(d -> d.addAsResource(new StringAsset("Hi!"), "templates/hi.html"));

@Inject
Template hello;

@Inject
Template hi;

@Test
public void testScanning() {
assertEquals("Hello!", hello.render());
assertEquals("Hi!", hi.render());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.extension.TestInstantiationException;

import io.quarkus.bootstrap.app.AdditionalDependency;
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
import io.quarkus.bootstrap.app.RunningQuarkusApplication;
Expand Down Expand Up @@ -76,6 +77,7 @@ public class QuarkusUnitTest
InvocationInterceptor {

public static final String THE_BUILD_WAS_EXPECTED_TO_FAIL = "The build was expected to fail";
private static final String APP_ROOT = "app-root";

private static final Logger rootLogger;
private Handler[] originalHandlers;
Expand All @@ -90,6 +92,8 @@ public class QuarkusUnitTest
private Path deploymentDir;
private Consumer<Throwable> assertException;
private Supplier<JavaArchive> archiveProducer;
private List<JavaArchive> additionalDependencies = new ArrayList<>();

private List<Consumer<BuildChainBuilder>> buildChainCustomizers = new ArrayList<>();
private Runnable afterUndeployListener;

Expand Down Expand Up @@ -164,12 +168,54 @@ public Supplier<JavaArchive> getArchiveProducer() {
return archiveProducer;
}

/**
*
* @param archiveProducer
* @return self
* @see #withApplicationRoot(Consumer)
*/
public QuarkusUnitTest setArchiveProducer(Supplier<JavaArchive> archiveProducer) {
Objects.requireNonNull(archiveProducer);
this.archiveProducer = archiveProducer;
this.archiveProducer = Objects.requireNonNull(archiveProducer);
return this;
}

/**
* Customize the application root.
*
* @param applicationRootConsumer
* @return self
*/
public QuarkusUnitTest withApplicationRoot(Consumer<JavaArchive> applicationRootConsumer) {
JavaArchive root = ShrinkWrap.create(JavaArchive.class);
Objects.requireNonNull(applicationRootConsumer).accept(root);
return setArchiveProducer(() -> root);
}

/**
* Add the java archive as an additional dependency. This dependency is always considered an application archive, even if it
* would not otherwise be one.
*
* @param archive
* @return self
*/
public QuarkusUnitTest addAdditionalDependency(JavaArchive archive) {
this.additionalDependencies.add(Objects.requireNonNull(archive));
return this;
}

/**
* Add the java archive as an additional dependency. This dependency is always considered an application archive, even if it
* would not otherwise be one.
*
* @param dependencyConsumer
* @return self
*/
public QuarkusUnitTest withAdditionalDependency(Consumer<JavaArchive> dependencyConsumer) {
JavaArchive dependency = ShrinkWrap.create(JavaArchive.class);
Objects.requireNonNull(dependencyConsumer).accept(dependency);
return addAdditionalDependency(dependency);
}

public QuarkusUnitTest addBuildChainCustomizer(Consumer<BuildChainBuilder> customizer) {
this.buildChainCustomizers.add(customizer);
return this;
Expand Down Expand Up @@ -259,7 +305,7 @@ public QuarkusUnitTest setAllowTestClassOutsideDeployment(boolean allowTestClass
return this;
}

private void exportArchive(Path deploymentDir, Class<?> testClass) {
private void exportArchives(Path deploymentDir, Class<?> testClass) {
try {
JavaArchive archive = getArchiveProducerOrDefault();
Class<?> c = testClass;
Expand All @@ -271,7 +317,11 @@ private void exportArchive(Path deploymentDir, Class<?> testClass) {
if (customApplicationProperties != null) {
archive.add(new PropertiesAsset(customApplicationProperties), "application.properties");
}
archive.as(ExplodedExporter.class).exportExplodedInto(deploymentDir.toFile());
archive.as(ExplodedExporter.class).exportExplodedInto(deploymentDir.resolve(APP_ROOT).toFile());

for (JavaArchive dependency : additionalDependencies) {
dependency.as(ExplodedExporter.class).exportExplodedInto(deploymentDir.resolve(dependency.getName()).toFile());
}

//debugging code
ExportUtil.exportToQuarkusDeploymentPath(archive);
Expand Down Expand Up @@ -423,7 +473,7 @@ public void close() throws Throwable {
try {
deploymentDir = Files.createTempDirectory("quarkus-unit-test");

exportArchive(deploymentDir, testClass);
exportArchives(deploymentDir, testClass);

List<Consumer<BuildChainBuilder>> customizers = new ArrayList<>(buildChainCustomizers);

Expand Down Expand Up @@ -477,12 +527,16 @@ public boolean test(String s) {

try {
QuarkusBootstrap.Builder builder = QuarkusBootstrap.builder()
.setApplicationRoot(deploymentDir)
.setApplicationRoot(deploymentDir.resolve(APP_ROOT))
.setMode(QuarkusBootstrap.Mode.TEST)
.addExcludedPath(testLocation)
.setProjectRoot(testLocation)
.setFlatClassPath(flatClassPath)
.setForcedDependencies(forcedDependencies);
for (JavaArchive dependency : additionalDependencies) {
builder.addAdditionalApplicationArchive(
new AdditionalDependency(deploymentDir.resolve(dependency.getName()), false, true));
}
if (!forcedDependencies.isEmpty()) {
//if we have forced dependencies we can't use the cache
//as it can screw everything up
Expand Down

0 comments on commit 442f14e

Please sign in to comment.