My last project is going into production in a couple of weeks, and it has been implemented using JSF. I started with JSF in good faith: it should be stable by now (it has been years out there), it is blessed by Sun and included as the de facto web framework in JEE 5.
Bullshit.

Keep in mind that my experience involves mainly the MyFaces implementation, but a big share of the issues we experienced are consequence of the spec design. These are the conclusions of a medium-sized JSF project (about 30 man-months), and big heaps of hard work. The start was simple: the component/renderer/validator/converter model is clean and easy to get up and running. The validation model works like a charm, JSP pages are quite minimal and you can get a working skeleton of your application in a snap.
Then the hell of linking pages together starts.
Faces saves the view state between requests, which is dumb if you are working with persistent data. If you dare to use Hibernate, expect all kind of funny behaviour as a request tries to initialize some lazy relationship of a bean retrieved like two clicks ago. If you must refresh data with each request where is the fun of using a stateful view? Remember that it cannot be disabled easily
When you are not working with persistent data (if you are living in a cave or developing wizard interfaces) there are two scopes to store model state: the session context, which raises concurrency issues and is not recommended by the Faces community, and the conversation/process/whatever context, which is not standard and imply installing shale or seam to put even more lipstick on the pig.
Storing anything in the request scope is like nailing your feet to the floor: experience may prove it was not a good idea. Redirect navigation rules lose all the GET params (in fact there is no supported way to do a redirect with params without using HttpServletResponse by hand) and saveState will only work if you do not leave the page.
There is an extensive funky feeling when developing with faces:
- If you modify a jsp page, the next call may reflect the changes (if you're lucky) or not reflect it at all (since there is still serialized view data in the server, remember?).
- If you redeploy your application, your state gets lost. It's not a big deal with normal applications, but remember that part about Faces serializing the view between requests? The next request will show you the page as the first time, with all the form values empty. Any POST params will be lost. No error message will be shown.
I do not have enough space here to explain the hell that developing a custom component is, so will leave a short summary: you will be fine if you are doing a simple stupid component that is shown as a single textfield, and there are big, red, horny demons in your way if you try anything more sophisticated. Example of simple: a field for monetary amounts with two decimals that shows the '$' symbol next to it and is always linked to the same validator and converter. Example of interesting: a date interval. There are too many details to be explained here.
The default view technology is JSP, even when no one in the real world would recommend it; instead, use Facelets, or Clay, or some other non-standard framework. Not trying to be sarcastic here, since Facelets is pretty good, but this complicates the hiring and education of the team and in fact invalidates the selling point of Faces 'being a standard'.
The spec is heavily based on two things: POST params and (ugh) forward navigation rules, that are default. In my previous life forward was the exception, not the norm.
There is no clear entry point to a page. It is expected that each access to a JSF page must come from another JSF page. If there was any weird requirement like including a link by e-mail to do in-depth linking, you must provide explicit support for this. For example, when trying to use request scope:
public class JsfBeanController {
/** linked in the jsf-config file to #{param.beanId} */
private Integer beanId;
private Bean bean;
public Bean getBean() {
if (bean == null && beanId != null)
bean = springBeanController.getBean(beanId);
return bean;
}
}
Which is awful, since getBean() will be called hundreds of times and in different situations. If you expect some way to discern the first page call from later ones (to initialize the bean), well, there is none. You could use a PhaseListener but it will be called for each and every page in the web application.
The whole thing of "avoid evil action paradigm" is a big deal in weblogic deployments, where once a transaction is closed it cannot be reused (provided it is not XA). Since there is no flow control over when the data is read, this is a pretty common scenario:
- getBean() is called -> calls a spring controller bean that retrieves the object from the database. Since this is done in a read-only transaction (fine-tuning the spring config), the transaction is still reusable.
- your action method doSomething() is called -> writes changes to the database via a spring transactional method. Once this transaction is closed, it cannot be reused
- From now on, any attempt to forward to another page will fail because weblogic will try to reuse a closed transaction. Your only way is to redirect to another page, with the previous concerns about losing any GET parameters
REST: ain't no need of stinkin' GET params. In MyFaces everything makes heavy use of javascript, even when it's not necessary. I am still looking for the reason why commandLink is puking a load of freakin' javascript stuff instead of a plain HTML link (something must be applicable to Faces and not to, say, webwork or stripes). MyFaces has two config params to disable javascript, none of them work at all.
As a consequence of this GET issue, your application will not be accesible since they are not giving a no-javascript way to make things work. The community is not giving any kind of reason, anyway; I have seen anything from "any modern browser must have javascript on" to "it is false that using javascript impedes accesibility".
The timezone section of the spec is brain dead. What is the first thing your operating system does when installing? ask for the time zone. What does the database use as default? The OS timezone. What does java use as default? The OS timezone.
JSF uses GMT (come on, click the link, it's a fun read).
Maybe it's me, but I like to think about The British Empire as a minority compared with, say, the whole world. This has been pointed several times and there is no intention of changing it, nor there is anybody in the MyFaces team willing to make this timezone behaviour optional. Everyone in the world who wants to show a date on a page must implement his own DateConverter with the correct timezone and set as default if they do not intend to deploy on London.
This is a good example of the general behaviour: the most common case are frequent questions in the forums, and the answers are really weird. If you pretend to make a silly CRUD example with a list of objects where you can select one row and modify/remove it, you will almost for sure end up using the nonstandard preservedatamodel tomahawk attribute, which by the way nobody but its creators understand, and keep it as part of the whole witch hunt kit.
The black magic feeling is general. My team has a oui ja board stapled to the debugger. Almost nothing in the framework throws a good old stack trace. Instead, warnings (if something) are the norm. You can be slippy and try to use #{bean.address.number} when there is no "bean", and it will not fail but warn (is there anybody looking for serious bugs at the server traces? Not in my world). It would have sense to implement things this way if Faces could automagically initialize beans when receiving requests, but it does not. It fails miserably with a "conversion error" (in fact, a NullPointerException) but without a trace or hint about the real problem.
I did not expect the injection framework to be something spectacular, but I tried to inject two beans in a cycle (A references B, B references A) and it did throw an exception. Why? I am not using constructor args injection, if you can detect the cycle then you surely could link the two beans together. Is it that big deal?
More candy: be warned that when you are using MyFaces, you can choose between serializing the views in the server or the client. If you are choosing the server, up to 20 views per user are going to be stored in the session (I suppose if you navigate more than that, "back" would only work 20 times). If not, keep in mind that the view is going to be serialized to the client and compressed and encripted (well, depends on the implementation but the standard strongly recommends the encription part), which is both bad for performance and bandwidth.
Being serious, I do not expect JSF to have any performance problems if your application has less than 100 users and a reasonable server. But I find kinda funny the way questions about large JSF applications are answered. I can remember threads in postgresql and jboss forums where nobody asked what "big" was.
The MyFaces implementation looks like a noob festival: each class I have looked at could be implemented with a third part of code. I considered contributing, but the whole thing must be redone from the ground up. No wonder ADF is still waiting to be merged. And they are dreaming about AJAX stuff instead of cleaning that mess.
My favourite: the MyFaces shared lib is redistributed twice, changing the package name (shared-impl and shared-tomahawk). The reason? to keep tomahawk separate from MyFaces, so you can include MyFaces in the app server and allow web applications that use another tomahawk release. Weblogic already ships with commons, antlr, etc, and that has never been a problem.
It's not all their fault either: the spec has holes the size of New Zealand. Everything is an empty abstract class: FacesContext, UIViewRoot, UIComponent, ExternalContext, everything. Interfaces are for wimsies. As an example, if you consider that UIComponent is fine but decide to rewrite its child UIComponentBase class (the parent of all component classes which provides basic behaviour) it's fine, but further ahead you will find that your UIViewRoot class must extend from the Faces abstract class that extends UIComponentBase. And that one cannot be avoided.
I'm not still done with the standard: they almost wrote code in the spec, all the tiny details in every method are described. For example, lots of methods do a couple of null checks at the beginning, so in a normal request you can accumulate hundreds of repetitive checks for null values on the same variables (checks of internal consistency that are mostly unneeded). Remember that UIComponentBase class we talked about earlier?
public void setValueExpression(String name, ValueExpression binding) {
if (name == null)
throw new NullPointerException("name");
if (name.equals("id"))
throw new IllegalArgumentException("Can't set a ValueExpression for the 'id' property.");
...
}
public String getClientId(FacesContext context) {
if (context == null)
throw new NullPointerException("context");
...
}
In conclusion, In my project we have spent spent huge amounts of time in something taken for granted , navigating between pages. My experience is that any other framework is simpler.
Want to compare times? More than three man-weeks have been spent fixing silly JSF navigation problems. A full CRUD AJAX interface with Spring MVC and prototype in the same project took four days, and there was no previous experience with Spring MVC. Well, it took a while to understand the design principles behind the spec, and I don't like it.
Well said.
ReplyDeleteI've bitched time and time again about the people who write these specs. They seem to have been conspiciously absent when God handed us OO.
The idiotic design means that in effect, you have one implementation. Even though technically we have two, they share so much that there's absolutely no room for innovation beyond a certain level, and all you can do is tag on bells and whistles.
It's the same group of idiots which initially screwed up the jsp spec and the servlet spec, and now are blessing JSF with their midas touch of shit.
So now we're in the ludicrous situation of having a supposedly MODERN spec require other non-standard extensions to make it vaguely usable (seam and friends). In a time where every other spec is moving the other way (ejb3, ws, most of EE 5) it's perplexing that JSF remains so firmly in idiotland.
Oops, didn't mean to post anonymously...
ReplyDeleteI agree with most of your comments with a slight caveat: Most of big issues you mentioned can be solved by JBoss Seam. Now, I understand the "standards" argument. But before you ditch JSF altogether (or is it too late already :)), perhaps you can have a look at Seam. See my comments with an code example here:
ReplyDeletehttp://www.michaelyuan.com/blog/2006/10/23/is-jsf-really-that-bad/
Completely agree. JSF seems to make simple things difficult and difficult things impossible! Had nothing but trouble with it from the start.
ReplyDeleteI dont see whats so hard about writing a web framework - theres hundreds and even the best (webwork 2 -IMO) is a pain the ass the use with jsp EL.
*Sigh* - I guess we're stuck with whats out there until something better comes along though ...
Are you still of the same opinion on JSF? It seams to me, that the complexity of this thing makes it very hard to image wide scale success with it.
ReplyDeleteThanks
Yep, Grant, things haven't changed since the time of this post.
ReplyDeleteThe only two bloggers I know that say anything nice about JSF are Michael Yuan and Gavin King (both JBoss members backing the Seam project). This is vaporware.
In my experience, there is nothing in JSF that makes it more productive than Struts 2 or Stripes. There is no extra features, just extra complexity and less performance.
Hi Ignacio.
ReplyDeleteI'm about to start a new web application and I'm looking into jsf.. Looking at the articles on the web it seams to be the best technology ever for web applications, and so I am thinking about using it. Your post is the first I see against jsf. Yes.. it seams very much more complicated than Struts, but I'm trying to learn it.. Now several months are over since your post.. and I'd like to ask if things have changed since your post?
Best regards.
This is sadly consistent with everything I've heard about it. Re: people for it; Gavin doesn't buy me that much credibility in terms of frameworks. Hibernate has a few strong core principles and then a great part of it is dealing with exceptions... Spring doesn't feel like that to me.
ReplyDeleteInteresting and impressive.
ReplyDeleteLet's see. JSF is the new standard.
It provides tags to write output, and xml to deal with navigation.
Unfortunatly, tags use is repalced by facelets or Clay.
And navigation by swf.
It provides the "component" to web. But it is not new and exists since Tapestry.
What a success...
After a few months struggling with JSF I closed the door and switched to Wicket. Not once did I miss any of those JSF (mis)features!
ReplyDelete'They seem to have been conspiciously absent when God handed us OO.' Haha. Best remark I've read this year!
ReplyDeleteYes, first JSF seams very much more complicated than technologies as Tapestry, Struts, Tapestry, etc. After learning the concepts and work out the real benefit, I think it seams to be the best technology ever for web. Some great features are: JSP independent, state management, UI-Components, etc.
ReplyDelete.... Yes, it is not a Struts or JSP hacking frammwork ....
i am still trying to find a way to avoid using any managed-beans and navigation-rules int he faces-config.xml, the reason is that our app has more than 600 jsp files, it means it will have at least 600 beans and 600 rules! so how big the xml will be?
ReplyDeletethey are part of the business logic, so why i do have to write them in xml not in java?
cant i use servlet/jsp plus the UI components only?
This comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeletemmarinschek, you obviously have a lot of things to say, but please I would be thankful if you blog about it somewhere else and refer to it here. This way we do not lower the S/N ratio and do not continue a thread that is already quite old. 18 posts are quite too much to be addressed as simple comments to a thread.
ReplyDeleteOh nice. Great way to deal with feedback; delete the poor guy's comments. Hey, at least you got your bullshit point of view across.
ReplyDeleteI've written huge enterprise class web based applications using JSF for the past 2 years, so I fail to see what you're whining about.
You (and Hani for that matter) are exactly the sort of people that shouldn't be programmers.
Go ahead and delete my post too.
I am closing this thread to comments because it has become visible enough to attract the kind of people who find flame wars appealing.
ReplyDeleteFor the record, I have developed java applications since 1.1 (2001), so you are right, I am not exactly a programmer. And I am not removing your last post because everyone has a right to be recorded on the web as a jerk.