Quantcast

Thymeleaf 3 upgrade / rendering

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
This post was updated on .
Hi,

I just moved from thymeleaf 2.1.4 to 3.0.5 by following the migration guide but I see a huge rendering issue. I don't know how this is related to Thymeleaf but my page takes now  sometimes 2 seconds more to get displayed correctly. In the beginning the scroll bar if almost the same heigth of the page then it decrease to get really small.  The page contains quite a lot of stock.

If I revert the migration to Thymeleaf 3 the problem does not occurs anymore (same css, same js).
The only thing that I change is some html to be html 5 compliant (remove void element end tag, raw text elements closing tag).

Don't know if this could be related but my page contains many fragments.

I tried removing all our css and javascript and I still have the problem.
Also, I tried adding a simple alert in my footer and the delay is really long before I see the alert.

* tested in IE 11 and firefox. Same behavior.

Any idea what could cause this ?

Thanks
David
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
Ok just found the issue.

My templates were separated in 2 folders (pages and fragments).
I was using 2 template resolvers and needed to set the "check existence" to true otherwise I had an exception when a fragment was trying to be resolve in the pageTemplateResolver.

// templateResolver.setCheckExistence(true);

I moved every templates into 1 folder and disabled the checkExistence property. Now it renders fast !
If I enable it again, it become super slow.

So now my question is, how can I put my files into differents folder without affecting the performance ?
I have over 50 fragments and 20 pages. Would like to have separated folder for clarity.

Thanks
David



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

danielfernandez
Administrator
You can use the "viewNames" property in view resolvers in order to specify the patterns that your template/fragment names have to match in order to be resolvable by each of the resolvers.

Note however that, if the problem is you have a very large amount of fragments being called in (and therefore their existence on the file system checked for each of them during resolution), enabling the template cache should make this only happen the first time you use each of those templates/fragments. From then on, once cached, there would be no need to resolve templates or fragments again. This is, unless your fragments need being hot-replaced and would not allow caching (which is definitely recommended otherwise)
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
Ok thanks I will try that. I don't have a pattern right now for my templates but I will add one to compare.

The template caching is disabled by default in our code (locally only for developpers). Our version is not deployed into our test server so I was not able to compare locally vs our real server with caching enabled.

Thank you.
David
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
In reply to this post by danielfernandez
Hi,

I don't see how to use the "viewNames" on the viewResolver.

This is my folder structure :

|- webapp
    |- WEB_INF
       |- templates
          |- fragments
           ----> frag1.html
          |- pages
           ----> page1.html

I have 1 view Controller.
        @Bean(name = "viewResolver")
        public ThymeleafViewResolver getViewResolver() {
                final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
                viewResolver.setTemplateEngine(getTemplateEngine());
                return viewResolver;
        }
2 template engine

        @Bean(name = "templateEngine")
        public SpringTemplateEngine getTemplateEngine() {
                final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
                templateEngine.setEnableSpringELCompiler(true);
                final Set<ITemplateResolver> templateResolvers = new HashSet<ITemplateResolver>(2);
                templateResolvers.add(this.getPageTemplateResolver());
                templateResolvers.add(this.getFragmentTemplateResolver());
                templateEngine.setTemplateResolvers(templateResolvers);
                return templateEngine;
        }

and 2 view resolvers

        @Bean(name = "fragmentTemplateResolver")
        public SpringResourceTemplateResolver getFragmentTemplateResolver() {
                final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
                templateResolver.setApplicationContext(this.applicationContext);
                templateResolver.setCheckExistence(false);
                templateResolver.setPrefix("/WEB-INF/templates/fragments/");
                templateResolver.setSuffix(".html");
                templateResolver.setTemplateMode(TemplateMode.HTML);
                templateResolver.setOrder(1);
                templateResolver.setCacheable(!EnvironmentVariable.checkForPresence(EnvironmentVariableEnum.LOCAL));
                return templateResolver;
        }

        @Bean(name = "pageTemplateResolver")
        public SpringResourceTemplateResolver getPageTemplateResolver() {
                final SpringResourceTemplateResolver templateResolver = new  SpringResourceTemplateResolver();
                templateResolver.setApplicationContext(this.applicationContext);
                templateResolver.setCheckExistence(true);
                templateResolver.setPrefix("/WEB-INF/templates/pages/");
                templateResolver.setSuffix(".html");
                templateResolver.setTemplateMode(TemplateMode.HTML);
                templateResolver.setOrder(0);
                templateResolver.setCacheable(!EnvironmentVariable.checkForPresence(EnvironmentVariableEnum.LOCAL));
                return templateResolver;
        }

Heres an example of how I include a fragment :

<th:block th:include="head :: Head" />

So I dont see how to use a pattern in my template resolvers except by renaming all my fragments and prefix them like  "frag_frag1.html" but I found this a little  bit oldschool...

"viewnames" seems to be related to the view resolver. Can we have many of them ? Seems like I need to renamed also all my templates and prefix them. Is there an other way than doing this ?

Thanks
David





Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
In reply to this post by danielfernandez
So I changed my resolver like so

        @Bean(name = "fragmentTemplateResolver")
        public SpringResourceTemplateResolver getFragmentTemplateResolver() {
                final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
                templateResolver.setApplicationContext(this.applicationContext);
                templateResolver.getResolvablePatternSpec().addPattern("fragments/*");
                templateResolver.setPrefix("/WEB-INF/templates/");
                templateResolver.setSuffix(".html");
                templateResolver.setTemplateMode(TemplateMode.HTML);
                templateResolver.setOrder(0);
                templateResolver.setCacheable(!EnvironmentVariable.checkForPresence(EnvironmentVariableEnum.LOCAL));
                return templateResolver;
        }

        @Bean(name = "pageTemplateResolver")
        public SpringResourceTemplateResolver getPageTemplateResolver() {
                final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
                templateResolver.setApplicationContext(this.applicationContext);
                templateResolver.setPrefix("/WEB-INF/templates/");
                templateResolver.setSuffix(".html");
                templateResolver.setTemplateMode(TemplateMode.HTML);
                templateResolver.setOrder(0);
                templateResolver.setCacheable(!EnvironmentVariable.checkForPresence(EnvironmentVariableEnum.LOCAL));
                return templateResolver;
        }

and changes all my th:include=" by  th:include="fragments/  but I still have the same issue.
It's the fact that having 2 template resolver that seems to cause the rendering issue.

If I put all my templates in the same foler, I dont have any issues.


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
In reply to this post by danielfernandez
Had to revert to thymeleaf 2 for now.

I have 2 problems with thymeleaf 3 that I don't know how to solve.
1. Using many resolver cause a rendering glitch. It Looks like the page is loaded by blocks. We see part of the page moving. Takes up to 2 seconds sometime to display the page. Not related to my js or css. Does the same thing if I remove all the js or css. If I revert to thymeleaf 2, the problem doesn't appears.

2. I have an issue with IE 11. Maybe it's related to the fact that the html is more strict (html 5) but
in some of my pages, I have request that never gets a response. The page is loading forever.
I try removing lots of html to see if there was something wrong somewhere but didn't find something specific. Problem occurs on "big" page. Adding 1-2 line of html reproduce the problem.. The same page works fine with thymeleaf 2...

Thanks
David
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

danielfernandez
Administrator
Hello David.

Would it be possible for you to provide us with a functional test project reproducing both issues?

Thanks,
Daniel.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

danielfernandez
Administrator
Oh by the way, one quick fix: both your template resolvers have order 0, so if checkExistence is false there is still a possibility that the wrong template resolver is still kicking in.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
Oh good point. Ill try that.
Not sure how i can provide a functionnal test. Im working on internal transactionnal application. I don't have nothing public. Our pages are separated by many fragments.
Ill check what I can do.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
In reply to this post by danielfernandez
So still have the same problem.

Finally, I have just one template resolver.

        @Bean(name = "templateResolver")
        public SpringResourceTemplateResolver getTemplateResolver() {
                final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
                templateResolver.setApplicationContext(this.applicationContext);
                templateResolver.setPrefix("/WEB-INF/templates/");
                templateResolver.setSuffix(".html");
                templateResolver.setTemplateMode(TemplateMode.HTML);
                templateResolver.setOrder(0);
                templateResolver.setCacheable(true);
                return templateResolver;
        }

        @Bean(name = "templateEngine")
        public SpringTemplateEngine getTemplateEngine() {
                final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
                templateEngine.setEnableSpringELCompiler(true);
                templateEngine.addTemplateResolver(this.getTemplateResolver());
                return templateEngine;
        }

        @Bean(name = "viewResolver")
        public ThymeleafViewResolver getViewResolver() {
                final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
                viewResolver.setTemplateEngine(getTemplateEngine());
                return viewResolver;
        }


For all my fragment, I include them by prefixing them with "fragments/myFragment :: MyFragment".


How does the template engine works ? Is there multiple interaction with the server for resolving a page template ? When I check my http request, I only see 1 POST (post/get/redirect pattern) and 1 GET containing the entire page but I feel like theres many interactions.

If I save my page (save as html) and open it the problem does not occur but when coming from the server I have the issue. With caching enable, my page take 779ms to be processed. So it does not look like a performance issue.

I try turning off the spel compiler but it's 200% worst. The page takes an eternity to load. That's why I think there many interactions with the server but I can't figure out since I don't see the request.

Logs doesn't help me much either. All I see is stuff related to the cache.

ex:
[EXPRESSION_CACHE][CACHE_MISS] Cache miss in cache "EXPRESSION_CACHE" for key "spel|nextMetadata.visible".
[EXPRESSION_CACHE][CACHE_REMOVE][500] Max size exceeded for cache "EXPRESSION_CACHE". Removing entry for key "spel|studentType.metadata.visible". New size is 500.
[EXPRESSION_CACHE][CACHE_ADD][500] Adding cache entry in cache "EXPRESSION_CACHE" for key "spel|nextMetadata.visible". New size is 500.


Any idea/clarification ? Thanks
David

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

danielfernandez
Administrator
Your configuration looks OK as a default one. I mean, it would be a good configuration for most people and there is no reason why you would have any kind of performance issue at the Thymeleaf side, given your templates seem to be local files.

When you are measuring times, are you taking these times from Thymeleaf's TIMER logger (set "org.thymeleaf.TemplateEngine.TIMER" to TRACE)? or what you are measuring is the entire request-response cycle? if it's the latter, please take into account that you are not measuring template execution, but rather your entire application (Spring controllers, database access, etc.)

As for HTTP calls, Thymeleaf does not perform any internal HTTP calls to retrieve your templates, from your configuration I see they are local resources. So they are directly read from the file system using Spring's resource resolution mechanism.

Spring's SpEL compiler affects the potential performance of the expressions used in your templates. If turning your SpEL compiler off makes the page run 200% slower, then I'm guessing you have a really, really huge number of ${...} expressions or these expressions execute some really complicated logic inside your Model objects. Also, note the SpEL compiler improved vastly from Spring 4.2 onwards.

Anyway, this point reached we would really need a reproduction project in order to know what is happening in your specific scenario. Something we could execute, verify your issue/s, and then debug and see what's going on there...

Regards,
Daniel
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
Thanks for your time !

The time I give was with the thymeleaf timer logger.

Yes we have a huge amount of expressions. Our pages contains many fragment that call other fragment and every thing is manage server side.

For example, all our inputs fields are included that way :

        <input th:fragment="InputFrag(fieldStr, fieldId)"
                th:with="attr=(${attr}!=null?${attr} + ', data-fieldType=InputFrag':'data-fieldType=InputFrag'), maxLength=(${maxlength}!=null?${maxlength}:''), size=(${size}!=null?${size}:'')"
                th:class="(${inputType} == 'radio' or ${inputType} == 'checkbox'?'':'form-control')"
                th:if="*{__${fieldStr}__.metadata.visible}"
                th:type="(${inputType}!=null?${inputType}:'text')"
                th:id="'fld_' + ${fieldId}"
                th:classappend="(${inputClass}!=null?${inputClass}:'')"
                th:placeholder="(${placeholder}!=null?${placeholder}:'')"
                th:disabled="*{__${fieldStr}__.metadata.readOnly}"
                th:title="*{__${fieldStr}__.metadata.tooltip}"
                th:attr="__${attr}__"
                th:field="*{__${fieldStr}__.value}"
                th:maxlength="(*{__${fieldStr}__.metadata.maxLength}!=null?*{__${fieldStr}__.metadata.maxLength}: ${maxLength} )"
                th:value="${value}"
                th:text="${text}"
                th:size="${size}"/>

That the example of an input text only but have this for all kinds of form elements. Every thing is in the bean, populated by the server. So yeah we have a lots of expressions :).
I just don't understand the link with thymeleaf and my UI glicth. With the new version, it's really faster but got that weird UI glicth...

Ill try to build a project to reproduce the issue. Could take a few weeks Im out of town for 2 weeks.

Thanks
David
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Thymeleaf 3 upgrade / rendering

divad91@hotmail.com
This post was updated on .
In reply to this post by danielfernandez
Ok so I had a little of spare time to test that problem again.

A few people of my job took a look also and we don't understand the behavior.
I included 2 pictures of the performance tool in chrome.
One running with thymeleaf 2.1.4 and the other one with thymeleaf 3.0.5. Same html page and data.

Thymeleaf2
Thymeleaf3

With thymeleaf 3, the server response is really fast compare to thymeleaf 2.   3.6 seconds vs 16.6 seconds.
But the problem I have and that I don't understand is that the rendering with thymeleaf 3 took 647ms and with thymeleaf 2 197.ms.  So the rendering is almost 3 time slower. Any idea of how this could be related to thymeleaf ?

I build a dummy demo project using the project
https://github.com/jmiguelsamper/thymeleaf3-spring-helloworld
You can download mine with my changes at https://www.dropbox.com/s/wuqzusvmrvyngc5/thymeleaf3-spring-helloworld-master.zip?dl=0
Basically, it's just 1 page that contains 7000 lines to parse.
It a bit hard to see because the response is really fast but with thymeleaf 3, you will see the yellow footer glicth a little bit with IE (worse in IE). With thymeleaf 2, the response is slow but the page load entirely in 1 block.
Again, same html page, data etc. Request content size, headers are exactly the same.

** The issue in the demo project is really not a big deal but in my project, it can take 2-3 seconds...
** I run this with eclipse luna and tomcat (via eclipse) and java 6

Thank for your time
David.
Loading...