Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using thymeleaf without Spring, Servlet Container, Controller specific logic #561

Closed
Volodymyr128 opened this issue Dec 16, 2016 · 18 comments

Comments

@Volodymyr128
Copy link

Volodymyr128 commented Dec 16, 2016

I have missed about 30 minutes trying to figure out how to generate email HTML body from my service. This is a scheduled task, not an API call - means no controllers and Spring integrations.
I have raw java and I want to process single *.html file with Thymeleaf. How to do that?

In other words, I need Thymeleaf analogy for Velocity example:

VelocityEngine ve = new VelocityEngine();
ve.init();
Template t = ve.getTemplate( "helloworld.vm" );
VelocityContext context = new VelocityContext();
context.put("name", "World");
StringWriter writer = new StringWriter();
t.merge( context, writer );

P.S. I've read this issue, it doesn't provide an answer. Both Thymeleaf doc and thymeleafexamples-gtvg are bound to controller logic, resolvers and other stuff I do not need.

@ultraq
Copy link
Member

ultraq commented Dec 18, 2016

You've basically got the solution already, but maybe a few more looks to the Javadocs could have helped you out for the processing methods that you needed. Anyway, I've created standalone Thymeleaf template engines several times, mostly for writing tests, but the only code I can give you a link/reference to is a Groovy project which creates a Thymeleaf engine and then uses it to process an XML file.

Here's the creation of the template engine: https://github.com/ultraq/rss-xml-generator/blob/master/Source/nz/net/ultraq/rss/RssXmlGenerator.groovy#L42-L53 That can probably be made into Java for your HTML use case like so:

TemplateEngine templateEngine = new TemplateEngine();
TemplateResolver templateResolver = new TemplateResolver();
templateResolver.setTemplateMode('HTML');
templateEngine.setTemplateResolver(templateResolver);

Then the processing happens in a method just below: https://github.com/ultraq/rss-xml-generator/blob/master/Source/nz/net/ultraq/rss/RssXmlGenerator.groovy#L64-L69 Again, as Java:

Context context = new Context();
context.setVariable("name", "World");
StringWriter stringWriter = new StringWriter();
templateEngine.process("HelloWorld.html", context, stringWriter);

stringWriter will then have the result of the processed template.

@Volodymyr128
Copy link
Author

Volodymyr128 commented Dec 19, 2016

@ultraq I get the next exception at runtime:

Caused by: java.lang.NoClassDefFoundError: org/thymeleaf/templateresolver/TemplateResolver
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
at java.lang.Class.newInstance(Class.java:412)
at org.springframework.scheduling.quartz.AdaptableJobFactory.createJobInstance(AdaptableJobFactory.java:58)
at org.springframework.scheduling.quartz.SpringBeanJobFactory.createJobInstance(SpringBeanJobFactory.java:74)
at com.rokittech.etdm.core.service.AutowiringSpringBeanJobFactory.createJobInstance(AutowiringSpringBeanJobFactory.java:21)
at org.springframework.scheduling.quartz.AdaptableJobFactory.newJob(AdaptableJobFactory.java:41)
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:127)
... 1 more
Caused by: java.lang.ClassNotFoundException: org.thymeleaf.templateresolver.TemplateResolver
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 10 more

@ultraq
Copy link
Member

ultraq commented Dec 19, 2016

Whoops, looks like I had an error in my example when translating from Groovy to Java. The TemplateResolver templateResolver = new TemplateResolver(); line needs to be replaced with a specific template resolver, and from the Thymeleaf 3 docs there are 4 to choose from: http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#template-resolvers You probably want to use ClassLoaderTemplateResolver or FileTemplateResolver, which can load templates from the classpath or the file system respectively.

@Volodymyr128
Copy link
Author

@ultraq I've replaced it with TemplateResolver templateResolver = new ClassLoaderTemplateResolver(); but still get an exception

@ultraq
Copy link
Member

ultraq commented Dec 19, 2016

The type declaration also needs to be changed - there is no TemplateResolver class in Thymeleaf 3: http://www.thymeleaf.org/apidocs/thymeleaf/3.0.2.RELEASE/org/thymeleaf/templateresolver/package-summary.html

@Volodymyr128
Copy link
Author

@ultraq in my pom.xml I use

<dependency>
   <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring4</artifactId>
    <version>3.0.2.RELEASE</version>
</dependency>

But should I include any other dependencies?

@ultraq
Copy link
Member

ultraq commented Dec 19, 2016

That dependency is Spring-specific, and given the title of this issue I don't think you want to use Spring. So you might want to change that to just the Thymeleaf core library only, which just has the artifactId of thymeleaf. All of the Thymeleaf artifacts are listed on the Downloads page: http://www.thymeleaf.org/download.html

@Volodymyr128
Copy link
Author

Volodymyr128 commented Dec 19, 2016

@ultraq I've changed my pom.xml dependency to:

<dependency>
    <groupId>org.thymeleaf</groupId>
     <artifactId>thymeleaf</artifactId>
     <version>3.0.2.RELEASE</version>
</dependency>

It still throws exception at runtime when I try to invoke TemplateUtils.processHTMLTemplate method:

java.lang.ClassNotFoundException: org.thymeleaf.templateresolver.ITemplateResolver

My full version of code:

package com.mydomain.utils;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import java.io.StringWriter;

public class TemplatingUtils {

    public static String processHMTLTemplate(String templateName) {
        TemplateEngine templateEngine = new TemplateEngine();
        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setTemplateMode("HTML");
        templateEngine.setTemplateResolver(templateResolver);
        Context context = new Context();
        context.setVariable("name", "World");
        StringWriter stringWriter = new StringWriter();
        templateEngine.process(templateName, context, stringWriter);
        return stringWriter.toString();
    }
}

@ultraq
Copy link
Member

ultraq commented Dec 19, 2016

Could you double check that the Thymeleaf JAR is on the classpath when executing? I copied your code above and made a basic main method that called your method with "HelloWorld.html" (I also made that template) and output it to the console and it worked if I managed to get the -classpath argument on the command line right. If I got it wrong, I would get ClassNotFoundException just like you. eg:

This would get an exception: java -classpath .:libraries com.mydomain.utils.TemplatingUtils

This wouldn't: java -classpath .:libraries/* com.mydomain.utils.TemplatingUtils

(Note: I'm on a Mac, so your path separator symbol may vary)

@ultraq
Copy link
Member

ultraq commented Dec 19, 2016

How are you running your program? I was using the plain old java command so had to manually specify the classpath as per the -classpath argument I showed above. If I got it wrong, then Java couldn't find the Thymeleaf JAR, and threw a ClassNotFoundException, just like you had. If I got it right, then Java found the JAR and I got no exceptions.

I don't know how to use Maven to run a program however - I only know how to use it to retrieve dependencies. If there is something like a mvn run, does it include all the dependencies in the classpath by default? (I would hope it does!) Is there also a way for it to output the full java command that it issues? It might be good to check that the Thymeleaf JAR is somewhere in the classpath.

Just to verify, I also unzipped the thymeleaf-3.0.2.RELEASE.jar to see if there is an org.thymeleaf.templateresolver.ITemplateResolver file in there, and there is, so the file definitely isn't missing. It looks like it's a classpath issue that needs to be figured out.

@Volodymyr128
Copy link
Author

Volodymyr128 commented Dec 19, 2016

@ultraq I checked my classpath, thymeleaf jar is there. I unpacked it and check - everything is there. Haven't any clue at this point, but that's look really as maven packaging issue. Thank you for your time and patience!

@ultraq
Copy link
Member

ultraq commented Dec 19, 2016

Not a problem @Volodymyr128. Just out of curiosity I googled "maven classnotfoundexception" and found a few StackOverflow posts which talk about making sure the scope for dependencies is correct, or to set additional configs if a plugin is being used to run the program, etc. It seems to be a common enough problem, so I hope there's a solution in those answers somewhere!

@Volodymyr128
Copy link
Author

Volodymyr128 commented Dec 20, 2016

@ultraq that appears that classpath issues were the problems of Intellij cahce. Once I overcome them I get

org.thymeleaf.exceptions.TemplateInputException: Template mode "HTML" has not been configured

Just to complete this issue, you should insert into example above

templateResolver.setTemplateMode("HTML5"); // HTML5 instead of HTML

@huffstler
Copy link

For those that may find this later on (like I did), I have a small working example.

pom dependencies

<dependencies>
    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf</artifactId>
        <version>3.0.11.RELEASE</version>
    </dependency>
    <!-- 
    If you don't like seeing messages in stdout from slf4j, add this dependency 
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-nop</artifactId>
        <version>1.7.25</version>
    </dependency> 
    -->
</dependencies>

resources/templates/greeting.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Name & Date HTML example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Hello, ' + ${name} + '!'" />
    <p th:text="'Today is ' + ${date} + '.'"/>
</body>
</html>

Application.java in hello package

package hello;

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import java.time.LocalDateTime;
public class Application {
    public static void main(String[] args) {
        TemplateEngine templateEngine = new TemplateEngine();
        ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        resolver.setPrefix("/templates/");
        resolver.setSuffix(".html");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setTemplateMode(TemplateMode.HTML); // HTML5 option was deprecated in 3.0.0
        templateEngine.setTemplateResolver(resolver);
        Context ct = new Context();
        ct.setVariable("name", "foo");
        ct.setVariable("date", LocalDateTime.now().toString());
        System.out.println(templateEngine.process("greeting.html", ct));
    }
}

@devashish234073
Copy link

For me it worked after including attoparser,javassist,log4j,ognl,slf4j,thymeleaf and unbescape libraries.

@a13a
Copy link

a13a commented Mar 17, 2022

@huffstler 's example should be included in the examples list

@kght6123
Copy link

kght6123 commented Apr 24, 2022

I made a example maven project for myself.
https://github.com/kght6123/thymeleaf-standalone

@mahozad
Copy link

mahozad commented Apr 25, 2022

Here is a related Stack Overflow post: How to use Thymeleaf to make only a simple Java app (without Spring).

Borrowing from @huffstler solution above for Java, here is the same solution for Kotlin:

import org.thymeleaf.TemplateEngine
import org.thymeleaf.context.Context
import org.thymeleaf.templatemode.TemplateMode
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver
import java.time.LocalDateTime

fun main() {
    val resolver = ClassLoaderTemplateResolver().apply {
        templateMode = TemplateMode.HTML
        characterEncoding = "UTF-8"
        prefix = "/templates/"
        suffix = ".html"
    }
    val context = Context().apply {
        setVariable("name", "Lind")
        setVariable("date", LocalDateTime.now().toString())
    }
    val templateEngine = TemplateEngine().apply {
        setTemplateResolver(resolver)
    }
    val result = templateEngine.process("greeting", context)
    println(result)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants