I am a very experienced Seam user. I have used Seam daily now for almost 3 years.
In this blog post, I will try to convince you to avoid the Seam Application Framework, and truly understanding when it should be used, and when it shouldn't.
What is Seam Application Framework?
Seam in Action says: "Seam is an application framework for Java EE. It provides a container that manages components and leverage's those components to tie the layers of an enterprise Java application together. Nestled within the Seam code base lies a handful of classes that comprise the Seam Application Framework. This "framework within a framework" is a specialized collection of component templates that effortlessly blanket the programming requirements of garden-variety web applications. Such tasks include performing create,read,update and delete (CRUD) operations on entity instances;querying for data; and developing JSF page controllers. You may be hesitant to give this spattering of classes much notice, but I assure you that they'll carry you a long way."
According to Dan Allen, he is encouraging you to check this "framework within a framework" out. However, though he is right, that there are benefits with this framework, I will prove that there are far more disadvantages than advantages and that he should have rewritten the last line to include that the framework is only really useful for small CRUD applications, and not something to leverage and build upon.
Way too many people are using this framework
If you have a look at the Seamframework.org forums, you will see tons of questions from people having problems and issues with the Seam Application Framework. Either a lot of people are developing small CRUD based applications, or there is a growing misconception about Seam and Seam Application Framework.
There are many people that are confused about this notion. They think that Seam == Seam Application Framework, and that they have to use it. And who can blame them? The seam-gen utility is a fantastic way of getting you quickly started. However, I have always encouraged people to only use the create-project and generate-entities part of the utility, and not the generate-ui part of it, this because most people I have talked with, are not developing small CRUD based applications, but rather medium sized enterprise apps, but since you get the Seam Application Framework for free, it is quite easy to extend and use it.
PROS
I did mention that there are a few pro's about using this framework, but it depends on people truly understanding the inner workings of the framework.
- Quick to get started
- Excellent for small models,presentation and proof of concept
- Great in-built pagination of lists
- Can @Override the methods and inherit classes to extend/tweak upon the framework
I also want to note, before I get spammed with comments about how wrong I am, that you can if you know what you are doing, speed things up. Details can be found here. However, this again requires some knowledge about Seam.
CONS
- Its a bit slower
- More abstraction which might not suit your requirements
- More difficult to use than the plain components. You need to "learn a new framework as well as Seam"
Consider this very very simple example. We have a SystemLog, and we want to display the log.
@Entity
public class SystemLog {
@Getter @Setter
@Id
@GeneratedValue
private Long id;
@Getter @Setter
@NotEmpty
private String description;
@Getter @Setter
@Column
private String category;
@Getter @Setter
@Temporal(TemporalType.TIMESTAMP)
private Date date;
@Getter @Setter
@Column
private String username;
@Getter @Setter
@Column
private String organization;
@Getter @Setter
@Column(length = 1024)
@Lob
private String details;
@Getter @Setter
@Column
private String severity;
@Getter @Setter
@Column
private String ipAddress;
}
We have an action class that used the EntityQuery of the Seam Application Framework.
@Name("systemLogQuery")
@MeasureCalls
public class SystemLogQuery extends EntityQuery<SystemLog> {
@Logger Log log;
@Override
public String getEjbql() {
return "select sys from SystemLog sys";
}
@Override
public List<SystemLog> getResultList() {
log.warn("Inside systemlog query getResultList");
return super.getResultList();
}
}
And this normal (non tweaked) seam component.
@Name("systemLogNormal")
@MeasureCalls
public class SystemLogNormal {
@Logger Log log;
@In EntityManager entityManager;
List<SystemLog> result;
public List<SystemLog> getResultList() {
log.warn("Inside systemLog normal resultList");
if(result == null)
this.result = entityManager.createQuery("FROM SystemLog").getResultList();
return result;
}
}
And a simple data table iterating over the elements
<h:dataTable value="#{systemLogNormal.resultList}" var="sys">
<h:column>
<f:facet name="header">
description
</f:facet>
#{sys.description}
</h:column>
</h:dataTable>
Now at first glance, I think the normal seam component is much cleaner and easier to read. Remember, this is a very simple example. In a normal real life example you would have extended the component to be much more complicated, and if you want to do this using the existing framework, you will need to find out what the framework already is doing, and correctly override those methods. Otherwise, you risk of doing the same thing twice.
If you ask your self what the @MeasureCalls annotation is doing, then have a look here, and scroll down to the second example. But a short description is that it measures the time of execution for a method.
When you run this example using the SystemLogNormal and the Query component, they are pretty much equal. However, you will see that the Query component is also calling the validate() method, which takes a few milliseconds to execute.
It is correct that a few milliseconds is hardly anything noteworthy, however this is a very very simple example, with very little data, and in a more realistic scenario, the number could possibly be different. Either way, the overall time difference is that the EntityQuery object is a few milliseconds slower.
13:58:24,224 WARN [SystemLogQuery] Inside systemlog query getResultList
13:58:24,228 INFO [STDOUT] Hibernate:
select
systemlog0_.id as id0_,
systemlog0_.category as category0_,
systemlog0_.date as date0_,
systemlog0_.description as descript4_0_,
systemlog0_.details as details0_,
systemlog0_.ipAddress as ipAddress0_,
systemlog0_.organization as organiza7_0_,
systemlog0_.severity as severity0_,
systemlog0_.username as username0_
from
SystemLog systemlog0_
13:58:24,239 WARN [SystemLogQuery] Inside systemlog query getResultList
13:58:24,257 WARN [SystemLogQuery] Inside systemlog query getResultList
13:58:24,334 INFO [TimingFilter] 2.201607 ms 1 EntityQuery.validate()
13:58:24,334 INFO [TimingFilter] 15.967185 ms 3 SystemLogQuery.getResultList()
13:58:31,647 WARN [SystemLogNormal] Inside systemLog normal resultList
13:58:31,648 INFO [STDOUT] Hibernate:
select
systemlog0_.id as id0_,
systemlog0_.category as category0_,
systemlog0_.date as date0_,
systemlog0_.description as descript4_0_,
systemlog0_.details as details0_,
systemlog0_.ipAddress as ipAddress0_,
systemlog0_.organization as organiza7_0_,
systemlog0_.severity as severity0_,
systemlog0_.username as username0_
from
SystemLog systemlog0_
13:58:31,662 WARN [SystemLogNormal] Inside systemLog normal resultList
13:58:31,678 WARN [SystemLogNormal] Inside systemLog normal resultList
13:58:31,712 INFO [TimingFilter] 15.740409 ms 3 SystemLogNormal.getResultList()
As you can see, we get three outputs of "Inside systemlog". Really there should only be one. So adding a @Factory annotation on top of it will do the trick for both versions.
Then there are the Home and Query objects, with methods like isIdDefined(), clearDirty(), isManaged(), getEjbql(), RESTRICTIONS and more confusing methods which you need to read the documentation to understand.
Conclusion
In my opinion, for first time users who are learning Seam, it is more than enough to understand and learn the framework, instead of in addition learning "a framework inside a framework". The seam-gen utility is sort of a devil in disguise. Its great for a shortcut to get started, but its evil in disguise for new comers.
Newbies think Seam Application Framework is Seam, and all components they create must extends the Home, List or Query objects. Its confusing, and in my opinion they should avoid using it unless they know all the ins and out of both Seam and the application framework.
So if you are new to Seam, start by looking at the Seam examples that comes with the bundle. They are far better to use, than generating the UI with seam-gen and going from there.
6 comments:
Whilst I agree with what you say, I think your title should be something more along the lines of "SEAM isn't the only solution!" or "What exactly is SEAM good for?"
I initially thought your post was about to read into a essay explaining it should never be used, but it seems you still advocate it's use in certain scenarios.
This is what I agree with.
In my opinion, SEAM is there for software development teams who need - or are forced to choose - an "Enterprise application platform".
For anything else, SEAM is overkill. You would use plain JavaEE to write a blog or a todo list would you?
Here's my approach!
* For the truly simple stuff: use Grails
* For more involved projects: use Wicket
* For Enterprise apps: use SEAM (with Wicket)
Nice Shervin. Just for curiosity: can you supply some improvements to Seam framework as a way to avoid the related issues described by yourself ???
Hi Arthur.
Thanks for you reply. It's very difficult to suggest improvements to a framework, because it depends on what the use case for your case.
For instance, if you only want the nice paging abilities of the EntityQuery, then what you can do is take that stuff out, and create your own "Framework" and extend that class. But I wouldn't do that.
I would either use it or not, and if I use it, then I would make sure I fully understand the implications of the framework and keep in mind optimising.
Great post, thanks!
I recently ran into problems with EntityQuery, because of I was using join fetch in a query. Turns out EntityQuery doesn't really support this and can return false result count. I needed to override the getCountEjbql(), something recommended also in the Seam reference.
In general, I try to avoid using EntityQuery component.
Hi.
Thanks for the comment. Yes this is exactly I feel you should avoid using this if you don't know all the ins and out of the Seam Application Framework. Best to start with normal Seam and go from there.
at first i taught u r gonna say about not using Seam :-)
but as u said : Seam is the awesomest framework ever , also EntityHome and entityQuery r such a great help for developing application faster but one thing that i'm concern about in seam is
DESIGN WELL, USE WELL
Post a Comment