tschuehly / spring-view-component

Server-side UI components with spring boot
MIT License
209 stars 11 forks source link

Component JTE not found #8

Open DevAlves1993 opened 1 year ago

DevAlves1993 commented 1 year ago

Hello, After a demo with the Spring View Component version of Jte, I noticed a bug. JTE views are not rendered.

After several investigations I found that the TemplateEngine class could not render because it could not find the components (it triggered the gg.jte.TemplateNotFoundException exception).

I suspect the gg.jte.resolve.CodeResolver interface, there would certainly need to be a new implementation of this interface which could be capable of consulting jte models in several different folders.

tschuehly commented 1 year ago

Hey do you have a demo project / could you make one. So I can reproduce your issue?

DevAlves1993 commented 1 year ago

Hi @tschuehly, I used the example jte defined in the project. You can try running it at your level to see that the index ('/) will return a 404 Not Found page.

tschuehly commented 1 year ago

Oh okay I'm currently not sure how I can manage the different template languages and test applications and examples. I will look at it when I'm back from Vacation.

DevAlves1993 commented 1 year ago

I found a solution to the problem, I had to create a new implementation of the CodeResolver class which can check the existence of Jte templates in several directories.

Below is the code for the CompositeCodeResolver class.

package com.training.conf;

import gg.jte.CodeResolver;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class CompositeCodeResolver implements CodeResolver {

    private final List<CodeResolver> codeResolvers;

    public CompositeCodeResolver(List<CodeResolver> codeResolvers) {
        this.codeResolvers = codeResolvers;
    }

    @Override
    public String resolve(String name) {
        var content = "";
        for (CodeResolver codeResolver : this.codeResolvers) {
            try {
                String resolve = codeResolver.resolve(name);
                if(Objects.nonNull(resolve)){
                    content = resolve;
                }
            } catch (Exception ex) {
                // ignore
            }
        }
        if(!content.isEmpty())
            return content;
        return null;
    }

    @Override
    public long getLastModified(String name) {
        long lastModified = -1;
        for (CodeResolver codeResolver : this.codeResolvers) {
            try {
                lastModified = codeResolver.getLastModified(name);
                if(lastModified > 0)
                    return lastModified;
            } catch (Exception ex) {
                lastModified = -1;
            }
        }
        if(lastModified != -1)
            return lastModified;
        throw new RuntimeException("Could not find template " + name);
    }

    @Override
    public List<String> resolveAllTemplateNames() {
        List<String> allTemplateNames = new ArrayList<>();
        for (CodeResolver codeResolver : this.codeResolvers) {
            try {
                allTemplateNames.addAll(codeResolver.resolveAllTemplateNames());
            } catch (Exception ex) {
                // ignore
            }
        }
        return allTemplateNames;
    }
}

I then had to create a Spring Bean TemplateEngine that would support the CompositeCodeResolver class to resolve the detection of Jte templates.

@Configuration
public class TemplateConfig {

    @Bean
    public ViewResolver viewResolver() {
        return new JteViewResolver(jteEngine(), ".jte");
    }

    @Bean
    public TemplateEngine jteEngine() {
        DirectoryCodeResolver directoryCodeResolverTemplate = new DirectoryCodeResolver(
                Path.of("src/main/resources/templates")
        );
        DirectoryCodeResolver directoryCodeResolverComponent = new DirectoryCodeResolver(
                Path.of("src/main/java")
        );
      /* 
       DirectoryCodeResolver directoryCodeResolverComponent = new DirectoryCodeResolver(
                Path.of("src/main/kotlin")
        );
        */
        CompositeCodeResolver compositeCodeResolver
                = new CompositeCodeResolver(
                        List.of(directoryCodeResolverTemplate,
                                directoryCodeResolverComponent));
        return TemplateEngine.create(
                compositeCodeResolver,
                Path.of("jte-classes"),
                ContentType.Html,
                this.getClass().getClassLoader()
        );
    }
}
DevAlves1993 commented 1 year ago

Thank you for taking the initiative to design this library, it is useful in the Spring Web development environment. 👍

tschuehly commented 1 year ago

This should work automatically. I will take a look when I'm back from vacation!

tschuehly commented 1 year ago

@DevAlves1993 I'm back from vacation and had time to investigate the issue, I actually could not reproduce this Issue. The JteAutoConfiguration in the JTE Spring Boot Starter creates a DirectoryCodeResolver according to the gg.jte.templateLocation = src/main/kotlin in the application.properties. The example itself doesn't use any templates in the resources directory.

But you are right the CompositeCodeResolver is needed in the JTE AutoConfiguration, because currently it doesn't look into the resources directory, thank you for that!

tschuehly commented 1 year ago

CompositeCodeResolver is needed for #10