Saturday, December 27, 2014

Trying Thymeleaf for one day: Why is it so hard?

In a project that uses fully stateless server-side frontend, I tried to use Spring MVC with Thymeleaf, using Spring Boot conventions. I've been using Apache Wicket for 2-3 years already so I'm definitely new with Thymeleaf ways, but I have some experience with Spring MVC. The reason why I wanted to use Spring MVC instead of Wicket is because if the app is entirely stateless, I won't be using like 90% of what Wicket is good for (statefulness and component-based framework).

My feelings? Since it's just one day there are definitely lots of things I might be missing out... but at this point I'm somewhat demotivated by Thymeleaf. Let me explain why.

Thymeleaf is a view layer with the goals of having great HTML5 support and local previews (without server). Note that I won't be describing Thymeleaf in detail, there's introduction tutorial to Thymeleaf if you like.

Basics

The first hours are good, there's good support for string manipulation, SpEL expressions, URI manipulations, and dynamic lists are easy:

<ul>
   <li th:each="place : ${places}">
       <a th:href="|/${localePrefId}/${placeSlugs[place.id]}|" th:text="${place.name}">Baso Enggal Malang</a>
   </li>
</ul>

The ${} @{} #{} and |...| (in some cases nested) syntax looks a bit arcane to me, but I can live with them, since I can't think of a better syntax anyway.

Layouts

This is my first uneasyness: layouts aren't built-in. However, you can either integrate with Apache Tiles 2 (why Tiles 2, not Tiles 3?) or use the Layout Dialect.

I'm not familiar with Tiles, although it looks great but I went with Layout Dialect, with the gut feeling that it's more "Thymeleaf native" than the Tiles integration. Sample templates for Gigastic:

The layout - layout.html

<div layout:fragment="content">
    Hello world!
</div>

The content - city.html


<div layout:fragment="content" class="about">
    <nav>
        <ol class="breadcrumb">
...
</div>

Ok, that's great! I'm happy. :)

Amazing Surprise: Automatic <head> elements

I was trying to find out how to propagate the HTML metadata tags (<title>, <meta name="description">, etc.) from the main template through the layout so they appear on the final rendered page. Imagine my delight when it's automatic! I haven't read anything about this feature on the Thymeleaf guide, but it works out of the box and I'm excited about it!

In Wicket you'd typically do something like:

<title wicket:id="title">Gigastic: Exciting Times Together!</title>
<meta wicket:id="metaDescription" name="description" content="Wow"/>

then back it in the superclass WebPage with:

add(new Label("title", getTitleModel()));
add(new MetaTag("metaDescription", getMetaDescriptionModel()));
...
protected abstract IModel<String> getTitleModel();
protected abstract IModel<String> getMetaDescriptionModel();

Nothing hard, but in Thymeleaf I can just do:

<title>Bandung, Jawa Barat, Indonesia</title>
<meta name="description" content="Informasi daftar tempat wisata di Bandung"/>

And they'll automatically replace the values from the layout. Neat!

Note that Wicket also has <wicket:head> but it only works for adding stuff into head, not for overriding them.

The Bad: Pagination

After that it goes downwards, at least in my opinion. For pagination, Spring MVC is nice, which can resolve the pagination URI parameters (page=, size= and sort=) as Pageable object which is directly usable:

@RequestMapping(Array("id/bandung"))
def bandung(model: ModelMap, pageable: Pageable) = {
  model.put("localePrefId", "id")
  model.put("pageable", pageable)
  val places = placeRepo.findAll(pageable)
  val placeSlugs = mapAsJavaMap(places.asScala
    .map { it: Place => it.getId() -> SlugUtils.generateSegment(it.getName()) }.toMap)
  model.put("places", places)
  model.put("placeSlugs", placeSlugs)
  log.debug("placeSlugs={}", placeSlugs)
  "city"
}

The Thymeleaf template, umm... I'm quite disappointed. First I wanted to know if there's already Thymeleaf-Bootstrap library project out there, which provides ready-to-use components like Bootstrap pagination. Nope, the first hit for "thymeleaf bootstrap pagination" is Yuan Ji's March 2013 article, which feels quite recent to me, and made me sad. Because at that time Wicket-Bootstrap would already be running circles around Thymeleaf (oh, with AJAX support too)! Even Wicket's built-in PagingNavigator is way ahead of this. But here it goes:

<div class="pagination">
    <ul>
        <li>
            <span th:if="${!places.hasPrevious()}" class="disabled">Prev</span>
            <a th:if="${places.hasPrevious()}" th:href="@{${#httpServletRequest.requestURI}(page=${places.number-1})}">Prev</a>
        </li>
        <li><span th:text="${places.number+1}">1</span></li>
        <li>
            <span th:if="${!places.hasNext()}" class="disabled">Next</span>
            <a th:if="${places.hasNext()}" th:href="@{${#httpServletRequest.requestURI}(page=${places.number+1})}">Next</a>
        </li>
    </ul>
</div>

As you can see I'm too lazy to implement all of Yuan Ji's code. Also, you can see some "logic" up there in th:if, but since it's simple boolean evaluation it's a simple logic that shouldn't be a problem in a template. In AngularJS I'd have done it the same way.

One thing not in my taste is the verbosity in getting the current page URI: ${#httpServletRequest.requestURI}. But again I can live with it, since it's easy to make my own variable anyway, but I wonder why they don't make this commonly used variable use shorter alias.

Now if I'm going to write that much code just for pagination, I'd better find a way to keep a library of them and reuse it in many pages, in various ways...

Custom Reusable Components/Tag Libraries? How about Extensibility?


You wish.

Good programmers know what to write. Great ones know what to rewrite (and reuse).  - Eric S. Raymond
Please, PLEASE, PLEASE prove me wrong, but I've come to the hypothesis that in practice it's not feasible.

The Thymeleaf way of reusability/extensibility is via dialects and processors.

The terms may sound foreign, but wait until you see how to dynamically change a tag's CSS class by using a Thymeleaf processor: (I try to be nice and reduce the lines of code visually, so I remove the comments)

public class ClassForPositionAttrProcessor
        extends AbstractAttributeModifierAttrProcessor {

    public ClassForPositionAttrProcessor() {
        super("classforposition");
    }

    public int getPrecedence() {
        return 12000;
    }

    @Override
    protected Map<String, String> getModifiedAttributeValues(
            final Arguments arguments, final Element element, final String attributeName) {
        final Configuration configuration = arguments.getConfiguration();
        final String attributeValue = element.getAttributeValue(attributeName);
        final IStandardExpressionParser parser =
                StandardExpressions.getExpressionParser(configuration);
        final IStandardExpression expression =
                parser.parseExpression(configuration, arguments, attributeValue);
        final Integer position =
                (Integer) expression.execute(configuration, arguments);
        final Remark remark = RemarkUtil.getRemarkForPosition(position);
        final Map<String,String> values = new HashMap<String, String>();
        if (remark != null) {
            switch (remark) {
                case WORLD_CHAMPIONS_LEAGUE:
                    values.put("class", "wcl");
                    break;
                case CONTINENTAL_PLAYOFFS:
                    values.put("class", "cpo");
                    break;
                case RELEGATION:
                    values.put("class", "rel");
                    break;
            }
        }
        return values;
    }

    @Override
    protected ModificationType getModificationType(final Arguments arguments, 
            final Element element, final String attributeName, 
            final String newAttributeName) {
        return ModificationType.APPEND_WITH_SPACE;
    }

    @Override
    protected boolean removeAttributeIfEmpty(final Arguments arguments,
            final Element element, final String attributeName, 
            final String newAttributeName) {
        return true;
    }

    @Override
    protected boolean recomputeProcessorsAfterExecution(final Arguments arguments,
            final Element element, final String attributeName) {
        return false;
    }
}

That's not including the dialect code, and the code needed to register the dialect into SpringTemplateEngine. (which isn't much, but I'll explain later why it could be problematic)

That seems way too much ceremony and low-level just to dynamically change the CSS class. Thymeleaf definitely could use a major improvement in this area. Compare this with how we do it Wicket style: (oh yeah, baby!)

final Remark remark = RemarkUtil.getRemarkForPosition(position);
final Map<String,String> values = new HashMap<String, String>();
if (remark != null) {
    String cssClass = null;
    switch (remark) {
        case WORLD_CHAMPIONS_LEAGUE:
            cssClass = "wcl";
            break;
        case CONTINENTAL_PLAYOFFS:
            cssClass = "cpo";
            break;
        case RELEGATION:
            cssClass = "rel";
            break;
    }
    
item.add(new CssClassNameAppender(cssClass));
}

The Wicket code is just one line, unobtrusive and doesn't distract you from the main logic (which form the bulk of the code). Oh and Wicket Behaviors are crazily reusable and extensible, too!

Also of note is that Thymeleaf processors doesn't seem to support Spring dependency injection but only @Resource injection, so you have to call appCtx.getBean(...) thing. Not a dealbreaker, but it's quite inconvenient (Wicket supports Spring dependency injection in many places, and pretty much anywhere with Injector.get.inject()).

The important thing is that by using dialects/processors, you lose what Thymeleaf prides in the first place: local previewability. Not that I care about this particular feature. In Bippo, after the initial mockup phase our designers always work with the development server (since 95% things that can look wrong only happens when you put in dynamic data and behavior anyway) and close to 0% of our templates are locally previewable, even though you can make Wicket templates locally previewable if you want to. But hey, if you boast so much about local previewability but then it's gone when you try to reuse stuff (which should be common use in software development), then the feature is as good as gone in the first place.

It was scary to me at this point, then I wondered if this really how it's done in the real-world... and the horror: it is. An evidence is from popular open e-commerce software Broadleaf Commerce's ContentProcessor.java. Broadleaf uses Thymeleaf templating and they write their own dialect using "blc" prefix, and lots of processors.

In Wicket-speak this is akin to having only onComponentTag() and no behaviors, no (Generic)Panels, and no subclassing of components. Sounds cumbersome to me.

But consider that I can make a custom dialect and all the processors I want, and assuming they're quite reusable to be put in a separate library to reuse across projects... are they extensible? By this I mean is possible to subclass a processor, override a method to change its behavior, and use it easily on a few pages?

It seems not. The whole processor architecture isn't designed to be re-extensible, by that I mean it's designed to extend Thymeleaf, but not to extend a processor further. Even if you try to extend it, the primitives are too low-level to make extensibility practical. This is apparent in the "flatness" of Broadleaf Commerce's processor hierarchy: it doesn't have a class hierarchy of processor, what it has are a few base processor classes and many separate processors.

A Thymeleaf processor is so primitive it has no concept of a model (the M in MVC), let alone a typesafe version of it. Also there's no way to easily extend a processor from an imported dialect and then use it. You have to create a custom dialect which lists the extended processor and then use it. Again, such ceremony on top of ceremony. And that's still HTML, it'd be even harder to add in AJAX behaviors in a reusable and extensible way with Thymeleaf.

Wicket has Behaviors, which are way ahead than this. And it's easy to have AJAX functionality as well, extensible, and pretty much the only ceremony is .add() and new SomethingBehavior() normal Java instantiation. Behaviors aren't the only way to have reusability, the primary reusability mechanism of Wicket is its entire fleet of component hierarchy, infinitely subclassable/extensible, with probably the most common (but in no way limited to) superclass is (Generic)Panel. And you can put them in a reusable and extensible library such as Soluvas Web.

Dandelion DataTables's Thymeleaf integration is another evidence:


Not only that the Thymeleaf version still forces the developer to add required HTML markup (which probably will be duplicated in many pages), but it's actually more verbose than the older JSP taglib-based technology it's supposed to supersede. Where's the DRY?

This is the most painful revelation for me against Thymeleaf, and it's the primary reason why then I'm looking to find an alternative to Thymeleaf, but before that there are some other things.

Bootstrap (and Bootswatch, and Font-Awesome, and...) Integration... and Taglibs

Formerly I tried to find any kind of Bootstrap-Thymeleaf integration, including the usual companions like Bootswatch, Font-Awesome, and the likes. I didn't find any worth including, for reasons that was unknown to me until I found out the reusability issue (I wanted to call it design flaw but it's probably by design) above.

Thymeleaf doesn't support JSP tag libraries (JSTL), since it breaks local previewability but in JSTL's place it has processors which can do some things JSTL can do plus some but in Thymeleaf-specific way and still breaks local previewability.

Having been spoiled by Wicket Bootstrap and Wicket Stuff, this is a huge letdown. :(

So I added a bunch of webjars to my build.gradle file and links to them in my Thymeleaf templates and that's when I get to the next point...

The CDN, URI References, Webjars, JavaScript and CSS Dependencies, <head> and </body> scripts, LESS, minification, aggregation...

How do I replace these <link rel="stylesheet"> and <script src=> URIs with CDN ones in production? To be fair, this isn't Thymeleaf's fault.

A StackOverflow answer recommends using a Properties file, which is okay I guess. I prefer Wicket Bootstrap's dedicated-class-per-JS-library and typesafe-way of doing this, but not a big deal.

The bigger deal is with dependencies. In Wicket, a Component/Behavior implements renderHead() which declares external JavaScript or CSS or custom inline JavaScript. These in turn may depend on other JavaScript/CSS files that will be merged in the rendered page. No JavaScript or CSS will be included twice. And the page doesn't need to care about them.

In Thymeleaf, these are just tags. Even if I implement Thymeleaf processors, I'm not sure whether it's possible to add JavaScript/CSS files automatically, let alone manage them. wro4spring-thymeleaf-dialect seems promising, but: it's a community project (Wicket's JavaScript/CSS management is built in core), the last commit was April 2013.

Also other things like LESS, minification, aggregation. To be fair, Wicket doesn't have these LESS and minification out of the box as well, but the foundation is there including resources bundles for aggregation and the rest are provided by Wicket Bootstrap which fortunately is presently well maintained.

Conclusion: Thymeleaf, FreeMarker, or...?

As I mentioned, after using Thymeleaf for one day and hitting a roadblock on reusability and extensibility I tried to find alternatives.

Struts 2: It's probably good, but I'm content with Spring MVC and Struts doesn't solve the view layer problem.

JSF / Tapestry / Vaadin: Having used JSF 2.1 in the past, I'm absolutely sure Wicket is superior. I've used Vaadin a bit, and heard good things about Tapestry. But the thing is, if I'm looking for a component-based framework I'd just use Wicket: I've invested there, both code and knowledge.

FreeMarker / Velocity / JSP: I've heard good things about FreeMarker as a template language, and I'd love an opportunity to use it. We at Bippo have been using Mustache for hypertext templating and it's cool too. Now FreeMarker with JSP taglibs would be a step up in the reusability front compared to Thymeleaf, but I doubt it will solve the other peculiarities like resource management I mentioned above. In a way it also feels backwards because we're back to JSP-land, since actually I liked how Thymeleaf does most things (except dialects/processors), although I definitely prefer older-but-working-tech to newer-but-less-functionality any day.

The surprising (for me) conclusion is that I think Wicket is ideal for this "stateless web app" use case:
  1. I'm well-versed in Wicket
  2. Wicket and all its components and custom components are straightforward reusable, no ceremony required
  3. Extensible, again no ceremony just good old "class A extends B" + @Override elementary OO thing
  4. Has excellent out-of-the-box coverage of commonly needed functionality, such as JavaScript and CSS resource management
  5. This guy Martin Grigorov is awesome, I have 27 Wicket JIRA issues and 22 of them are resolved, usually within a few days. :)
  6. Excellent ecosystem for integration with Bootstrap, Bootswatch, Font-Awesome, LESS, and other various JavaScript libraries. And if I need to do it myself, it's easy and reusable.
  7. I'll leverage my existing knowledge and Wicket component libraries, both for stateful and stateless apps, without maintaining two separate projects with similar functionality, and two separate parts of my brain.
  8. In any case I need stateful stuff, it's there.
Note that this post is my findings plus opinions, it's not my intention to attack Thymeleaf in any way, in fact I quite liked it other than the major (and a few minor) weaknesses I outlined above. If anything isn't accurate due to my limited knowledge of Thymeleaf or you have solutions to my problems, please do let me know in the comments and I'll correct/update them. Thanks! :)

30 comments:

Chang bui thi said...

You topic is very great and useful for us…thank you
Signature:
i like play games friv online and play games2girls Download baixar facebook

Anonymous said...

Hi Hendy
We used Wicket for a long time too. For three years.
Now we decided to try Thymeleaf + SpringMVC.
Man, it's awful!
I'm trying to create a reusable component like a DropDownChoice so now i'm starting to hate that template engine.

Frozen Juegos said...

Very interesting! Thanks you
Signature:
Jugar juegos de frozen gratis en línea gratis, los nuevos de princesa de Disney juegos frozen - la princesa encantadora y linda. Divertirse frozen!

Facebook Descargar said...

Thank you for your post, I look for such article along time, today i find it finally. this post give me lots of advise it is very useful for me
Signature:
download descargar facebook gratis para Android celular and download free descargar facebook apk en español, descargar facebook plus , descarga facebook

thị hậu nguyễn said...

Great! Thanks for sharing the information. That is very helpful for increasing my knowledge in this field.
happy wheels
super mario bros
pacman
agario

Yepi said...

You need to kill time, you need entertainment. Refer to our website. hope you get the most comfort.
Thanks you for sharing!
Yepi
Kizi
Friv
Friv Games

Online Friv said...

you'd have time to look these kids active. Please visit our website and let us play the game interesting.
Thanks for sharing !
Friv 10
Kizi 10
Yepi 2

Friv5 said...

You need to kill time, you need entertainment. Refer to our website. hope you get the most comfort.
Thanks for sharing !
Friv 5
Kizi 1
Yepi 3

oakleyses said...

burberry handbags, prada outlet, ray ban sunglasses, gucci handbags, tiffany and co, louis vuitton, christian louboutin outlet, christian louboutin uk, tory burch outlet, ray ban sunglasses, polo ralph lauren outlet online, louis vuitton outlet, jordan shoes, michael kors outlet online, nike air max, christian louboutin, ray ban sunglasses, louis vuitton outlet, longchamp outlet, prada handbags, michael kors outlet online, uggs outlet, christian louboutin shoes, uggs on sale, ugg boots, louis vuitton outlet, chanel handbags, longchamp outlet, oakley sunglasses, nike outlet, louis vuitton, uggs outlet, polo outlet, michael kors outlet, burberry outlet, tiffany jewelry, michael kors outlet online, oakley sunglasses, nike air max, ugg boots, michael kors outlet online, oakley sunglasses, replica watches, nike free, michael kors outlet, replica watches, longchamp outlet, kate spade outlet

oakleyses said...

sac vanessa bruno, hollister uk, polo lacoste, vans pas cher, nike roshe run uk, nike free uk, true religion outlet, air max, polo ralph lauren, coach purses, louboutin pas cher, hollister pas cher, kate spade, lululemon canada, timberland pas cher, michael kors outlet, sac longchamp pas cher, michael kors pas cher, ray ban pas cher, true religion outlet, abercrombie and fitch uk, nike air max uk, replica handbags, nike air max uk, nike blazer pas cher, coach outlet, nike roshe, michael kors, mulberry uk, coach outlet store online, burberry pas cher, guess pas cher, nike air max, sac hermes, michael kors, north face uk, hogan outlet, north face, converse pas cher, oakley pas cher, ralph lauren uk, true religion outlet, new balance, jordan pas cher, nike free run, nike tn, longchamp pas cher, true religion jeans, nike air force, ray ban uk

oakleyses said...

sac vanessa bruno, hollister uk, polo lacoste, vans pas cher, nike roshe run uk, nike free uk, true religion outlet, air max, polo ralph lauren, coach purses, louboutin pas cher, hollister pas cher, kate spade, lululemon canada, timberland pas cher, michael kors outlet, sac longchamp pas cher, michael kors pas cher, ray ban pas cher, true religion outlet, abercrombie and fitch uk, nike air max uk, replica handbags, nike air max uk, nike blazer pas cher, coach outlet, nike roshe, michael kors, mulberry uk, coach outlet store online, burberry pas cher, guess pas cher, nike air max, sac hermes, michael kors, north face uk, hogan outlet, north face, converse pas cher, oakley pas cher, ralph lauren uk, true religion outlet, new balance, jordan pas cher, nike free run, nike tn, longchamp pas cher, true religion jeans, nike air force, ray ban uk

oakleyses said...

sac vanessa bruno, hollister uk, polo lacoste, vans pas cher, nike roshe run uk, nike free uk, true religion outlet, air max, polo ralph lauren, coach purses, louboutin pas cher, hollister pas cher, kate spade, lululemon canada, timberland pas cher, michael kors outlet, sac longchamp pas cher, michael kors pas cher, ray ban pas cher, true religion outlet, abercrombie and fitch uk, nike air max uk, replica handbags, nike air max uk, nike blazer pas cher, coach outlet, nike roshe, michael kors, mulberry uk, coach outlet store online, burberry pas cher, guess pas cher, nike air max, sac hermes, michael kors, north face uk, hogan outlet, north face, converse pas cher, oakley pas cher, ralph lauren uk, true religion outlet, new balance, jordan pas cher, nike free run, nike tn, longchamp pas cher, true religion jeans, nike air force, ray ban uk

oakleyses said...

hollister, north face outlet, mac cosmetics, baseball bats, nike huaraches, babyliss, new balance shoes, iphone cases, chi flat iron, louboutin, ferragamo shoes, iphone 5s cases, longchamp uk, soccer shoes, mcm handbags, nike roshe run, timberland boots, mont blanc pens, vans outlet, bottega veneta, instyler, oakley, ralph lauren, valentino shoes, giuseppe zanotti outlet, s6 case, p90x workout, celine handbags, iphone 6s plus cases, herve leger, north face outlet, nike trainers uk, ipad cases, iphone 6s cases, lululemon, wedding dresses, nike air max, abercrombie and fitch, nfl jerseys, hermes belt, insanity workout, ghd hair, asics running shoes, beats by dre, iphone 6 cases, reebok outlet, jimmy choo outlet, soccer jerseys, hollister clothing, iphone 6 plus cases

oakleyses said...

pandora jewelry, canada goose uk, canada goose, barbour, moncler, karen millen uk, swarovski crystal, replica watches, louis vuitton, canada goose, louis vuitton, juicy couture outlet, nike air max, lancel, swarovski, canada goose jackets, gucci, converse, thomas sabo, canada goose outlet, pandora charms, ugg,uggs,uggs canada, juicy couture outlet, doudoune moncler, hollister, moncler, montre pas cher, coach outlet, links of london, hollister, ugg uk, ugg, toms shoes, marc jacobs, pandora uk, vans, canada goose, moncler, canada goose outlet, moncler outlet, moncler outlet, louis vuitton, moncler, ray ban, louis vuitton, louis vuitton, converse outlet, moncler uk, ugg,ugg australia,ugg italia, supra shoes, barbour uk

Priya Tamil said...

Wow ! another interesting articles,i am so amazed after visit that post.easy to observed all provided articles,your explanation was clearly implemented.These information really worth saying.
java training

Friv4 said...

When you're tired, you want to relax after a stressful working hours, you need to have time to take care of the kids active.
Please visit our website and play exciting flash games.
Thanks you for sharing!
Friv 4

Kizi200 said...

Play the Best Free Games! We've picked out the racing games, cooking games, candy crush, games shooting, fashion games, ...
Thanks for sharing !
Kizi 200
Friv 1
Y88
Y9

Trúc Phương said...

in kỷ yếu giá rẻ
in phong bì giá rẻ
mẫu thẻ nhân viên đẹp
In thẻ VIP giá rẻ
mau ky yeu dep
mau phong bi dep
báo giá in kẹp file
in giấy khen
in bao li xi
in thiep chuc mung nam moi
in catalogue

Friv100 said...

You want to relax after a stressful working hours. Refer to our website. hope you get the most comfort.
Thanks for sharing !
Friv 100
Friv 8
Friv 2

Raja chidambaram said...

use full
mobogenie pc
snapchat pc
snapchat for pc
mobogenie for pc
uc browser for pc

GirlsGoGames Dressup said...

You want to relax after a stressful working hours. Refer to our website. Hope you get the most comfort.
Thanks for sharing !
GirlsGoGames
Kizi 100
Frvi

lee anh nuooi said...

Play games we reward Corolla Altis car

Friv Games
Kizi 100
Games2Win
Kizi 5

Alice Denny said...

Welcomes to google terminal keep sharing such ideas in the future as well.
google snake this was actually EARN TO DIE
what i was looking for,and i am glad to came here! AGE OF WAR 2
Hi! I’ve been reading your blog for a while HAPPY WHEELS
I want you to thank for your time of this wonderful read!!! Slitherio
Amazing insight you have on this, Slither io This article always blew me BIG FARM

Abiya Carol said...

It's interesting that many of the bloggers to helped clarify a few things for me as well as giving.Most of ideas can be nice content.The people to give them a good shake to get your point and across the command .

PPC Services in Chennai

games2girls said...

You need to have time to take care of the kids active. Please visit our website and play exciting flash games.
Thanks you for sharing!
Games2Girls
Kizi 1000
Frin
Friv 3

Unknown said...

Hi Hendy
Thanks for this nice article, i use recently thymeleaf with layout dialect in a spring MVC application, but for every change i bring in my web page html or even css i should restart my tomcat. So i can't really take advantage of it. Do you have any idea ? thank you

nguyen thanh truc said...

There are things lost and I feel sorry that I did not appreciate. But there are also new things and I lost relieved realized: Yeahhhhh, and I stress, yet
Friv Games
Y2
Friv 200

for IT the said...

I have read your blog its very attractive and impressive. I like it your blog.

Java Training in Chennai Core Java Training in Chennai Core Java Training in Chennai

Java Online Training Java Online Training Core Java 8 Training in Chennai Core java 8 online training JavaEE Training in Chennai Java EE Training in Chennai

Xplorant Seo said...

Wonderful blog.. Thanks for sharing informative blog.. its very useful to me..

iOS Training in Chennai

trump Ivanka said...

Work anybody but take some time to relax every day work efficiency will be higher than that. Invite you to visit our website

Friv 1
GirlsGoGames
Friv 4 School

Post a Comment

 
Copyright 2009 Spring vs Java EE Web Dev. Powered by Blogger Blogger Templates create by Deluxe Templates. WP by Masterplan