JSF 2.0 with Spring 3 and Spring Security 3 on Google Application Engine
The technologies used are :
- JSF 2.0 - Mojarra 2.0.2
- Spring 3.0.0.RELEASE
- Spring Security 3.0.0.RELEASE
- Hibernate Validator 4.0.2.GA as reference implementation of JSR 303: Bean Validation
- AspectJ 1.6.7
- Groovy and the gmaven-plugin
You can see a live demo at : http://sandcode.appspot.com
UPDATE(22 August 2010)
You can compare with : http://deltaset-demo.appspot.com
and see the project created based on the ideas in that post : http://www.deltaset.org
The application is build with Maven 2.2.1
For local execution you would need Google App Engine SDK
The archetype was updated on 11 February 2010, so that works for latest Google App Engine - 1.3.1
If you want to play and customize it you would need to have installed and configured Java, Maven and Google App Engine SDK
Choose gae-archetype after running :
mvn archetype:generate -DarchetypeCatalog=http://sandcode.googlecode.com/svn/maven2/
build the application :
mvn install
run it :
dev_appserver target/YOUR_ARTIFACT_ID
type in your browser: http://localhost:8080
enjoy :)
If you want to extend the application you can use the small generation utility coming with it :
mvn -Pcrud -Ddomain=Book -Dfields=title,author,remarks
The generator will create for you :
src/main/java/YOUR_EXAMPLE_PACKAGE_NAME/app/domain/Book.java
src/main/java/YOUR_EXAMPLE_PACKAGE_NAME/app/repository/BookRepository.java
src/main/java/YOUR_EXAMPLE_PACKAGE_NAME/app/web/BookController.java
src/main/webapp/secured/book/list.xhtml
src/main/webapp/secured/book/detail.xhtml
src/main/webapp/secured/book/form.xhtml
It will also extend the menu :
src/main/webapp/resources/app/menu.xhtml
with :
<app:menu-group groupname="GEN ADDED - Book ">
<app:menu-item itemoutcome="/secured/book/list" itemvalue="#{msg['book.view.list']}">
<app:menu-item itemoutcome="/secured/book/form" itemvalue="#{msg['book.view.form']}">
</app:menu-item>
Extend the application default Messages bundle :
src/main/resources/Messages.properties
with:
#labels generated for Book
book.view.detail=GEN Book detail
book.view.form=GEN Book form
book.view.list=GEN Book list
book.title=GEN Title
book.author=GEN Author
book.remarks=GEN Remarks
This was the 2 min intro.. depending on your internet connection you can have a nicely running GAE application for about 2-5 min :)
If you want to deploy it on your account of Google Application Engine you will have to edit by hand :
src/main/webapp/WEB-INF/appengine-web.xml
and then type :
appcfg update target/YOUR_ARTIFACT_ID
And... Some more... :
The application is having the following features :
layerd architecture - domain, service, controller
'request' based crud for JSF
localized - currently supports English and Bulgarian. To add another language you need to :
1. create a translated copy of Message.properties
2. edit faces-config.xml
3. download from http://www.famfamfam.com/lab/icons/flags/ the correct flag ;) and add it to src/main/webapp/resources/img/flag with the right name.
4. create a translated copy of ValidationMessages.properties (needed by Bean Validation)
Basic UI customization - the user can change the preffered language, the position of the menu and the colors of the application.
Using of JSF 2.0 custom components, templating.
Using of "JSR 303: Bean Validation".
Using of "JSR 330: Dependency Injection for Java" annotations with Spring 3 ( javax.annotation.ManagedBean , @Inject, @Named ).
Using of Java Config style for Spring 3 ( @Configuration ).
Using of @Bean annotation with inplace implementation of an interface, directly in the @Configuration bean.
Using of @RolesAllowed (javax.annotation.security.RolesAllowed) with Spring Security 3 for securing your application.
Using of 2 distinct roles ROLE_USER and ROLE_ADMIN, securing of methods called with h:commandLink (or h:commandButton)
'Native' integration between Spring Security and JSF : app/web/LoginBean.java ; src/main/webapp/login.xhtml
Almost NO xml configuration for spring : src/main/webapp/WEB-INF/applicationContext.xml
AspectJ for logging app/aspect/TimeExecutionLoggingAspect.java (well.. by default it is swithced off, but you can take a look in the pom.xml )
AspectJ and the Spring stereotype annotations (Repository, Service, etc..) can help you easy log only what you need : app/aspect/SystemArchitecture.java
AspectJ for static weaving of 3rd party jar files (in our case this is jsf-impl , because of a bug in the local Google App SDK ) : common/aspect/FixWebConfigurationAspect.java
Groovy gmaven-plugin and Groovy Templates for generation of files..
Using of box-shadow and border-radius css properties for IE : src/main/webapp/resources/css/gray.css
Enjoy :)
And I'm definitly waiting for your feedback, questions and proposals ;)
The best application ever!
ReplyDeleteThanks :)
ReplyDeleteI accidentally took a look at the applicationContext.xml at http://sandcode.googlecode.com/svn/tags/BLOG_JAN2010/crud-jpa-spring-gae/src/main/webapp/WEB-INF/applicationContext.xml and the schema is for Spring 2.5???
ReplyDeleteМного добро много добро :Д
ReplyDeleteСупер :Д
RE: Toy
ReplyDeleteyou are right , the schema is the one for Spring 2.5 . But, that is one of the great things about Spring - they are backward compatible. Also the configuration of beans with annotations is available exactly since version 2.5. Spring 3.0 adds the support for JSR 330 - "Dependency Injection for Java" , and not only that.. Thanks for the interest in the post :)
Hi Dimitar,
ReplyDeletesuperb post, nice recipe with fresh ingredients!
The use of aspects is fantastic just as Chris Richardson lesson.
Among the others I've appreciate very much this snippet in pom
UTF-8
Regards
Hi Domenico,
ReplyDeletethanks a lot for the good words :)
I'm happy that you have enjoyed the post.
Hi,
ReplyDeleteThanks for this really good post.
I try to start from scratch without Maven, and i have a problem.
When i try to access a managedbean, i have a null error. I see a log at Jetty startup :
JSF1027: [null] The ELResolvers for JSF were not registered with the JSP
If you have a solution, thanks.
Hi, what is the startup time of the app? I have similar setup with jsf2 and it took about 15s to render first page. Thanks Luba
ReplyDeleteHi Luba,
ReplyDeletethe "cold" startup time for that app is between 18-23 sec according to the log files, which shouldn't worry you at all. If you are having enough users, it will stay "warm" and the response times are round 180ms to 500ms depending on the executed back-end logic. If you want to keep the application "warm" you can implement some "cron" task, but this is not needed if you have enough users.
Hi Richard,
ReplyDeletethe problem with your app could be anywhere - versions of jar files, web.xml, faces-config.xml . I would recommend you to take some of the existing maven archetypes, or just working sample, and then to modify it to fit your needs.
Hi Makariev,
ReplyDeleteThanks for your answer.
I have found my error. I have used a bad @ManagedBean (javax.faces), instead of javax.annotation. So spring cant make this job ;)
Now, application works but, yes there is a but ;)
I have a managed bean whith a list. When the list is called by some XHTML, my logger show me that getter is called 8 times ...
I'm beginner on JSF2, but i think is not a normal lifecycle ?
Thanks
Excellent post!!, i enjoy read it.
ReplyDeleteKernelman, Montreal(CA)
Hi Makariev,
ReplyDeletewhen I deploy it, error appears:
No persistence unit with name 'person-pu' found
but when I move the configuration to application-context.xml and append this line "" on pulEntityManagerFactory, it works fine.
any idea how POJO configuration cannot find the persistence.xml?
the line appended is persistenceXmlLocation in application-context, it works fine, but on POJO it doesn't work as if it cannot find the path of persistence.
ReplyDeleteHi Kahlil,
ReplyDeleteno idea what might be wrong with your deployment. The live demo application on the top of the post is exactly what I've described here.. and it works :) If the problem is on your local system, that could be something with the local environment : jdk version, gae version..
Bravo, Makariev!
ReplyDeleteI especially adore how you do this:
"AspectJ for static weaving of 3rd party jar files (in our case this is jsf-impl , because of a bug in the local Google App SDK ) : common/aspect/FixWebConfigurationAspect.java"
This is a very good use of AspectJ, and I'm glad you used it!
No more monkey-patching sources... (although that AOP is exactly monkeypatching, but what I mean is no need to change the original JAR)
Keep up the good work! Waiting for more of your quality posts.
P.S.: can you add in PrimeFaces 2.0 please??
You should disable snapshots for the repository definitions.
ReplyDeleteHi Hendy,
ReplyDeleteThanks for the good words :),
I've added your proposal for disabling snapshots - definitely important for faster build :)
This is by far the most useful post concerning Spring and App Engine I have found to date. And everything using the most recent version.. A true dream come true. Thank you SO much for giving back to the community!
ReplyDeleteExcellent demo!
ReplyDeleteHello Makariev,
ReplyDeleteWhat a great post you have with every piece of technology that're current today. Not only that but with the right layering architecture implementation example. Thank you so much!
However, I am trying to run it since this morning in the following environment:
1. Glassfish v3
2. JDK 1.6
But I get the following errors:
Start Up Error
===================
INFO: com.jsfdemo_jsfdemo_war_1 was successfully deployed in 9,302 milliseconds.
INFO: JSF1027: [/jsfdemo] The ELResolvers for JSF were not registered with the JSP container.
INFO: 18:51:09,065 WARN [org.springframework.web.context.request.FacesRequestAttributes] - Could not register destruction callback [org.springframework.beans.factory.support.DisposableBeanAdapter@492f0b] for attribute 'preferencesBean' because FacesRequestAttributes does not support such callbacks
The following errors are when I try to log in with user1/user1
================================================
INFO: 18:51:09,065 WARN [org.springframework.web.context.request.FacesRequestAttributes] - Could not register destruction callback [org.springframework.beans.factory.support.DisposableBeanAdapter@492f0b] for attribute 'preferencesBean' because FacesRequestAttributes does not support such callbacks
INFO: 18:52:43,089 WARN [org.springframework.web.context.request.FacesRequestAttributes] - Could not register destruction callback [org.springframework.beans.factory.support.DisposableBeanAdapter@711d75] for attribute 'loginBean' because FacesRequestAttributes does not support such callbacks
INFO: 18:52:51,039 WARN [org.springframework.web.context.request.FacesRequestAttributes] - Could not register destruction callback [org.springframework.beans.factory.support.DisposableBeanAdapter@e996af] for attribute 'loginBean' because FacesRequestAttributes does not support such callbacks
INFO: Instantiated an instance of org.hibernate.validator.engine.resolver.JPATraversableResolver.
INFO: 18:52:51,068 WARN [org.springframework.security.authentication.event.LoggerListener] - Authentication event AuthenticationSuccessEvent: user1; details: null
INFO: 18:52:53,264 WARN [org.springframework.web.context.request.FacesRequestAttributes] - Could not register destruction callback [org.springframework.beans.factory.support.DisposableBeanAdapter@74e65f] for attribute 'person' because FacesRequestAttributes does not support such callbacks
SEVERE: Error Rendering View[/secured/person/list.xhtml]
javax.faces.FacesException: javax.el.ELException: /secured/person/list.xhtml @10,56 pageInfo="#{person.pageInfo}": java.lang.NullPointerException: No API environment is registered for this thread.
...
...
Caused by: javax.el.ELException: /secured/person/list.xhtml @10,56 pageInfo="#{person.pageInfo}": java.lang.NullPointerException: No API environment is registered for this thread.
at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:107)
at javax.faces.component.UIComponentBase$AttributesMap.get(UIComponentBase.java:2166)
... 86 more
Caused by: java.lang.NullPointerException: No API environment is registered for this thread.
at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppId(DatastoreApiHelper.java:67)
at com.google.appengine.api.datastore.DatastoreApiHelper.getCurrentAppIdNamespace(DatastoreApiHelper.java:77)
at com.google.appengine.api.datastore.Query.(Query.java:84)
I would really appreicate if you could help me figure out what's causing this?
Thank you.
T
Hi Dimitar,
ReplyDeleteThank you for doing this. Getting Spring & GAE running through Maven is awesome. However I would like to ask if you could add Eclipse integration to your archetype. I tried it by simply using mvn eclipse:eclipse, but when I imported the project into eclipse, it was not recognized as a GAE application. Would you know how to make this work? Thanks for your help.
Regards,
Mitch
Very nice post...thanks for saving hell lot time of mine...
ReplyDeleteHi.. I loved the sample.. I am studying these technologies and your sample is completed.
ReplyDelete=)
What did you describe in the start is how access the code? There is other way to access? because I never use maven .. so.. if possible tell us =]
What would Spring Web Flow do for this project?
ReplyDelete