0

Environment:

  • AS: Wildfly-16.0.0.Final
  • JVM: jdk-11.0.2

We are implementing a Bean to be used as a freemarker Template provider. The Bean *.java file is contained in the source folder of the application-services-ejb JAR module, which is packaged in the application-services-ear EAR module.

The Bean constructor initializes a freemarker.template.Configuration object. Afterwards, the class which uses the Bean can invoke a Bean.getCredentialCreationTemplate() method, which obtains the desired Template from the Configuration object.

The Configuration object requires the directory containing all the *.ftl files to be passed to the Configuration.setDirectoryForTemplateLoading(File) method.

The FTL template directory is retrieved with the usual getResources() approach:

    String emailTemplateFolderPath = getClass().getClassLoader().getResource("emails").getFile();
    File emailTemplateFolder = new File(emailTemplateFolderPath);

Here comes the quirk: the emailTemplateFolderPath contains a wrong path: C:\Users\myusername\Desktop\content\application-services-ear-VERSION.ear\org.company-application-services-ejb-VERSION.jar\emails

The wrong part is the one stressed in bold. Indeed, the part in italic matches both the expected and the actual path within the EAR (I inspected the archive manually). Instead, I have no idea of where the bold part comes from. The directory C:\Users\myusername\Desktop\content does not even exist on my workstation, neither it resembles the actual EAR location, which is in a completely different path in my filesystem.

The only connection with my Desktop is that my Wildfly startup script and my application redeploy script are located in C:\Users\myusername\Desktop.

Why is the classloader failing in correctly locating the EAR? How can I fix?

Here is my code:

@Stateless
public class FTLTemplateProviderImpl implements FTLTemplateProvider {

    public enum FTLTemplate {
        CREDENTIAL_CREATION("credential-creation.ftl");

        private final String fileName;
        private FTLTemplate(String fileName) {
            this.fileName = fileName;
        }
        public String getFileName() {
            return this.fileName;
        }
    }

    private Configuration cfg = null;

    public FTLTemplateProviderImpl() {
        try {
            cfg = new Configuration(Configuration.VERSION_2_3_28);
            String emailTemplateFolderPath = getClass().getClassLoader().getResource("emails").getFile();
            File emailTemplateFolder = new File(emailTemplateFolderPath);
            cfg.setDirectoryForTemplateLoading(emailTemplateFolder); // <-- HERE IT BREAKS!
            cfg.setDefaultEncoding("UTF-8");
            cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            cfg.setLogTemplateExceptions(false);
            cfg.setWrapUncheckedExceptions(true);
            cfg.setTemplateLoader(new FileTemplateLoader(emailTemplateFolder));
        } catch (IOException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
    }

    @PermitAll
    @Override
    public Template getCredentialCreationTemplate() {
        try {
            return cfg.getTemplate(FTLTemplate.CREDENTIAL_CREATION.getFileName());
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }
}

Please take into account that we also tried to turn the @Stateless bean into a @Singleton, making all the necessary adjustment to make it loadable and injectable into the client bean. Although this, the content of emailTemplateFolderPath is exactly the same.

iccuta
  • 165
  • 1
  • 13
  • Please share folder structure – Abhijeet May 09 '19 at 03:20
  • 2
    As a general rule you should never attempt to access class loader resources as File objects because they are embedded in the archive and are not separate objects in your file system. It looks like you should be using a `ClassTemplateLoader` instead of a `FileTemplateLoader` – Steve C May 09 '19 at 05:55
  • Thank you, @SteveC! Actually, while waiting for some help, I was actually trying a similar solution, after some investigation about resources and the vfs protocol, and after realizing that the invocation of getFile() was leading me to the wrong direction. I'll let you know when it works – iccuta May 09 '19 at 10:55
  • Thank you @SteveC! By taking your answer as a suggestion, I switched to the **Configuration.setClassLoaderForTemplateLoading** method. Now, the *Configuration* object gets set up correctly and the *Template* object is successfully retrieved from the *Configuration*. If you want, you can post your answer and I will mark it as accepted. Moreover, if you allow me to, I will add some additional details in the comments about the **vfs://** vs **file:// URLs** problem, which makes the **setDirectoryForTemplateLoading** method **unusable** when dealing with resources bundled in a classpath JAR – iccuta May 09 '19 at 11:15

0 Answers0