Spring Boot with JSF/Primefaces
I’m a big fan of Spring Boot. If you need to bootstrap a Java Enterprise application in a short space of time its an excellent project to get you moving and within a few lines you can expose or consume RESTful services, initiate an embedded Tomcat servlet container and auto-configure your way into the whole Spring ecosystem with barely a line of XML in site (yes, there’s still the Maven POM).
This leads us to a new application design paradigm, that of the micro-service or application. Each web app in a self-contained jar, its own embedded web-server with its own configuration running in its own JVM instance sitting behind a web proxy presenting a suite or cloud of services to the outside world seemingly as a single product.
You can of course use Spring Web with Thymeleaf or simple JSP with AngularJS but I wanted to demonstrate how to build a web interface to such a service using JSF and Primefaces and have posted a fully functional demo application to GitHub:
This an Eclipse Maven Spring Boot project, get it built and running and navigate to http://localhost:9090 and have a look at its behaviour. The data is also exposed as a RESTful service using Spring Web’s @RestController
at http://localhost:9090/service/books/ and individual items on http://localhost:9090/service/book/0/.
The embedded Tomcat container has very little functionality of its own, JSF capabilities are bestowed by adding the appropriate JSF and Primefaces dependencies to the Maven POM followed by additional annotated bean definitions to augment the Spring Boot auto-configuration. Although Tomcat is using java config it still requires the presence of a web.xml
file and a faces-config.xml file in the webapp/WEB-INF
directory. Any configuration that would normally be done in the web.xml
file is done in a class that implements ServletContextInitializer
such as our class Initializer
in the example which must in turn be announced to the Spring Boot SpringApplicationBuilder
during bootstrap.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@EnableAutoConfiguration @ComponentScan({"com.oakdalesoft.bootfaces"}) public class Application extends SpringBootServletInitializer { @Value("${init.json}") private String init; public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(Application.class, Initializer.class); } } |
We now have two different types of containers active, one Spring container and a J2EE Servlet container (Tomcat) and really its better to keep them separate in your mind and technically but if we are going to transfer values between them we need some sort of interop bridge. Spring provides this through expression language via a SpringBeanFacesELResolver
which is referenced in your faces-config.xml
and injected by Spring for you. Now you can access Spring beans in your managed faces bean, passing vales both ways. See class BookModel
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
package com.oakdalesoft.bootfaces.view; import com.oakdalesoft.bootfaces.domain.Book; import com.oakdalesoft.bootfaces.persistence.BookRepository; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; import javax.faces.bean.RequestScoped; import javax.faces.context.FacesContext; import java.util.ArrayList; import java.util.List; /** * Created by Alex on 07/03/2015. */ @ManagedBean(name = "model", eager = true) @RequestScoped public class BookModel { public void setBook(BookView book) { this.book = book; } public BookView getBook() { return book; } @ManagedProperty(value = "#{book}") private BookView book; public BookRepository getBookRepository() { return bookRepository; } public void setBookRepository(BookRepository bookRepository) { this.bookRepository = bookRepository; } @ManagedProperty(value = "#{bookRepository}") BookRepository bookRepository; public String doCreateBook() { Book created = new Book(); created.setId(this.book.getId()); created.setTitle(this.book.getTitle()); created.setPrice(this.book.getPrice()); created.setnbofpage(this.book.getnbofpage()); created.setDescription(this.book.getDescription()); Book newBook = this.bookRepository.save(created); FacesContext.getCurrentInstance().addMessage("errors", new FacesMessage(FacesMessage.SEVERITY_INFO, "Book created", "The book " + created.getTitle() + " has been created with id=" + newBook.getId())); this.book.setTitle(""); this.book.setPrice(null); this.book.setDescription(""); this.book.setIllustrations(false); this.book.setnbofpage(null); return "index.xhtml"; } public void doFindBookById() { Book found = bookRepository.findOne(this.book.getId()); this.book.setId(found.getId()); this.book.setTitle(found.getTitle()); this.book.setPrice(found.getPrice()); this.book.setnbofpage(found.getnbofpage()); this.book.setDescription(found.getDescription()); this.book.setDescription(found.getDescription()); } public List<BookView> findAllBooks() { List<BookView> books = new ArrayList<BookView>(); for(Book entity : this.bookRepository.findAll()) { BookView view = new BookView(); view.setId(entity.getId()); view.setTitle(entity.getTitle()); view.setPrice(entity.getPrice()); view.setnbofpage(entity.getnbofpage()); view.setDescription(entity.getDescription()); view.setDescription(entity.getDescription()); books.add(view); } return books; } } |
Everything else in the project is simply a demonstration of the power of Spring Boot and how easy it is to add features thanks to auto configuration. The project includes:
- Spring Data JPA
- Spring Rest
- Spring Actuator
- Jackson Repository Populators
- URL Rewriting
Details of all of these can be found in the Spring Boot documentation except the last item, URL Rewriting. The one thing I hate about JSF is the ugly URL schema it leaves you with. This can be remedied by adding OcpSoft’s Rewrite Project to your project. I’m bringing this up now because once you’ve added the dependency to your POM the configuration requires a little special Spring Boot configuration voodoo too.
If you recall I mentioned that the web.xml
file is not loaded by the embedded Tomcat server and that all configuration is done through java config, well that includes activating the RewriteFilter
bean. This is performed in the class that extends SpringBootServletInitializer
:
1 2 3 4 5 6 7 8 |
@Bean public FilterRegistrationBean rewriteFilter() { FilterRegistrationBean rwFilter = new FilterRegistrationBean(new RewriteFilter()); rwFilter.setDispatcherTypes(EnumSet.of(DispatcherType.FORWARD, DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR)); rwFilter.addUrlPatterns("/*"); return rwFilter; } |
From here on you just need to define a HttpConfigurationProvider
and annotate it with the @RewriteConfiguration
annotation as per the documentation.
See also: The AngularFaces Project See Also: JSF on Spring Boot (Understanding Scopes)