- Introduction
- Out Of Scope
- Explanation of Hello World in JSF
- Explanation of how to setup JSF in eclipse
- Explanation of Java EE welcome file
- Explanation of JSF and Spring integration
- Explanation of ManagedBean vs Component vs Named scopes
- Explanation of Spring Data JPA and Hibernate
- Explanation of PrimeFaces DataTable
- Explanation of JSF Form tag
- Explanation of JSF bean scopes
- Explanation of PrimeFaces update
- Explanation of JSF messages and growl
- Explanation of JSF Validation With Hibernate Validator
- Explanation of complete JSF CRUD application
- Explanation of PrimeFaces Confirm Dialog
- Explanation of PrimeFaces Dialog
- Technologies Used
- Prerequisities
- Commands
- Contribution
- References
- Contact Information
The aim of this project is to show case the different ways in how Spring can be used together with JSF and PrimeFaces. For this project, a site monitoring tool is envisioned in which one can enter the name of the web site and its URL. One can also edit and later delete any entries from the data table. For displaying entries in a data table, Prime Faces are used.
- Attention: This project does not contain the latest dependencies in pom.xml.
This readme file contains explanation regarding various sections which are completed during the development of this project. Here, only important pints are highlighted as bullet points.
Since only basic implementation of data table from Prime faces is targeted for this project and the main idea is just to see them in action, unit tests are out of scope. Similarly, only certain prime faces components are used(namely: Growl, Modal Dialog and Messages), rest of the components are out of scope.
The important points in this section are as follows:
-
In case web.xml is not present in the project, it can be generated via Deployment Descriptor: primefaces ---> Generate Deployment Descriptor Stub.
-
The command to run on terminal is: mvn jetty:run
-
The index page is available at: http://localhost:8080/index.xhtml
The important points in this section are as follows:
-
How to make intelliSense/Code assist work for JSF(Follow everything defined in this section of the readme file)? Help ---> Eclipse Marketplace ---> Search for 'JBoss Tools' ---> We selected luna ---> In the next window, we only select JSF.
-
Right click on Project ---> Configure ---> Add JSF Capabilities ---> Click on 'Further Configuration Required' link ---> Select Type: Disable Library Configuration ---> Uncheck 'Configure JSF Servlet in deployment descriptor'(Reason: We already did it in web.xml)
-
Close all open files ---> Open index.xhtml file with JBoss Tools HTML Editor.
The important points in this section are as follows:
- How to make the index.xhtml file the default start page when the jetty server starts? Open web.xml file ---> Under tag, only allow
<welcome-file>index.xhtml</welcome-file>---> run the jetty server using 'mvn jetty:run' ---> Open localhost:8080 ---> default page is the index.xhtml page.
The important points in this section are as follows:
-
The reason we do not specify version for spring-web artifact Id is because the version number is already defined in dependencyManagement---> spring-framework-bom
-
What to configure in faces-config.xml? ---> Added an application ---> el-resolver tag in faces-config.xml file like the following:
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
This helps in integration between JSF Runtime and Spring.
- The initialization of the spring application is done as follows:
// TODO: Please change the class name to something more professional.
public class MyWebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext container) throws ServletException {
// Create the 'root' spring application context
// This is done via registering SpringCOnfiguration class which
// contains the annotations: @Configuration and @ComponentScan
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(SpringConfiguration.class);
// Manage the life cycle of the root application context
// This is achieved via ContextLoaderListener
container.addListener(new ContextLoaderListener(rootContext));
}
}- How to used @ManagedProperty annotation in the controller class?
// Using expression language
// Here, we are using Spring Bean name. By default, it is the same as the class
// name.
@ManagedProperty("#{helloSpringService}")
private HelloSpringService helloSpringService;Please note that if the setter for the service bean is not present, the following error occurs when running the application.
There is a possibility that the following error occurs:
Unable to create managed bean helloController. The following problems were found: - Property helloSpringService for managed bean helloController does not exist. Check that appropriate getter and/or setter methods exist.
To counter this, please create a setter method as follows:
/**
* This is needed so that JSF runtime can inject the instance in the variable.
* Otherwise, it will throw an exception on running the project.
*
* @param helloSpringService
*/
public void setHelloSpringService(HelloSpringService helloSpringService) {
this.helloSpringService = helloSpringService;
}The important points in this section are as follows:
-
Using @ManagedBean along with ManagedProperty("#{helloSpringService}") means that beans are managed by JSF Runtime. However, if we use @Component along with @Autowired, then beans are managed by Spring framework.
-
ManagedBean is created for each request i.e. it has request scope whereas @Component is Singleton.
-
In order to make @Compomnent behave with a request scope, we need to add @Scope("request").
-
If we want to achieve the same result in CDI context, then we need @Named and @Inject.
Initialization of Beans:
JSF Runtime:
@ManagedBean
public class HelloController {
// Using expression language
// Here, we are using Spring Bean name. By default, it is the same as the class
// name.
// @ManagedProperty("#{helloSpringService}")
private HelloSpringService helloSpringService;
public String showHello() {
// return "Hello from the Managed bean";
return helloSpringService.sayHello();
}
/**
* This is needed so that JSF runtime can inject the instance in the variable.
* Otherwise, it will throw an exception on running the project.
*
* @param helloSpringService
*/
public void setHelloSpringService(HelloSpringService helloSpringService) {
this.helloSpringService = helloSpringService;
}
Spring context:
@Component
public class HelloController {
@Autowired
private HelloSpringService helloSpringService;
// further lines of codeCDI context:
@Named
public class HelloController {
@Inject
private HelloSpringService helloSpringService;
// further lines of codeThe important points in this section are as follows:
-
https://vocado.tistory.com/entry/javalangExceptionInInitializerError-comsuntoolsjavaccodeTypeTags-%EC%97%90%EB%9F%AC ---> This fixed the compilation problem with lombok.
-
In this project, we are using HikariCP connection pool to make connections to the database and HSQLDB (HyperSQL Database) is used as relational database for this project.
-
Service is created during application start-up since it is a singleton.
-
https://stackoverflow.com/questions/53690136/the-import-javax-annotation-postconstruct-cannot-be-resolved ---> This explains the problem with @PostConstrcut annotation not being recognized.
-
https://stackoverflow.com/questions/6054692/error-creating-bean-sessionfactory ---> How to avoid hibernate problem with the creation of entitymanagerfactory
The important points in this section are as follows:
- Consider the following code:
<p:dataTable value="#{checkListController.checks}" var="check">
<p:column headerText="name">
#{check.name}
</p:column>
<p:column headerText="url">
<a href="#{check.url}" target="_blank">
#{check.url}
</a>
</p:column>
</p:dataTable>-
Here p:dataTable creates a JSF type datatable. It is populated via #{checkListController.checks} which is iterated over and the individual value is stored in var="check".
-
p:column represents a JSF style column with the name of the column represented via headerText="name". There is no special tag used for displaying row in the example.
The important points in this section are as follows:
-
<h:form> generates HTML form.
-
<p:panelGrid columns="2"> generates a HTML table with two columns.
-
<h:commandButton action="" value="save" />, this generates a save button.
How to bind the JSF front-end with the Controller Backend:
The important points in this section are as follows:
-
On the front-end side:
-
Input for url are captured via value="#{checkListController.check.url}" in input field.
-
upon clicking save button via <h:commandButton value="save" action="#{checkListController.save()}"/>, the request is send to the back-end.
-
Attention: By default, the form is not cleared automatically when save button is clicked. Why? Because, out of the box, the prime faces command button component sends the request to the server using Ajax Post. Hence, when we refresh the page, we will see the current state of the data from the database.
-
On the back-end side:
-
A check object is created.
Attention: By default, all JSF objects are initialized with null hence, we must call new operator to initialize the object.
- We call the save method in Controller ---> this will call the save() method in Service ---> this uses repository to save the element.
The important points in this section are as follows:
-
Why is @managedBean inefficient? Because @ManagedBean is created again and again on every request to the server. Hence, even when saving a new entry into the database, a select command on the database is executed first.
-
@ApplicationScoped: Singleton.
-
@SessionScoped: A bean with session scope is created per user.
-
@ViewScoped: It is created when the user access the page and it is still in memory when the page calls using Ajax to server, It is only destroyed when the user leaves the page. Why does the class implements Serializable? Because the information is stored in a Session and hence to have it from one request to the next, it must implement serializable.
-
We use javax.faces.bean.ViewScoped as import class as it works together with ManagedBean. javax.faces.view works with CDI context which we are not using.
The important points in this section are as follows:
- In order to make data from text box disappear after save button is clickwed, add update="checkForm" in save button.
Attention: This must match the form id.
- In order to dynamically update the data table, we need to add update="checkForm, :checkTable" in save button. Here, we add , to indicate that this is an additional component and : since this component is outside the form. ---> On the back-end side, in CheckListController class, we add checks = checkService.findAll(); in save() method so that the data is reloaded not only at system start-up but also after save button is pressed.
The important points in this section are as follows:
- In order to display the success message on save, we added the following line in CheckListCokntroller.save() method.
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO, "Check saved.", null));Additionally, one must add it as a value of update attribute in the save button with a : since the component is outside of the HTML form.
- On the front-end side, add
<p:messages id="messages"/>Attention: The messages tag name must be exact. In case the 's' at the end is missing, one would get a NullPointerException but it will not say that the component name is incorrect.
- Using <p:growl /> (Of Mac style based) provides a much nicer save message.
The important points in this section are as follows:
- Using standard Hibernate validator for checking fields here. One can also think of using javax.validations.
The important points in this section are as follows:
- For editing, we created a separate column which contains a command button:
<p:commandButton value="edit" action="#{checkListController.setCheck(check)}" update=":checkForm"/>Here, action="#{checkListController.setCheck(check)}" allows us to fetch the current check value from the Controller class whereas update=":checkForm" loads the current record in edit form.
- For removing a record, we created the following command button:
<p:commandButton value="remove" action="#{checkListController.remove(check)}" update=":checkTable, :messages"/>Here: action="#{checkListController.remove(check)}" calls the remove() method in check controller whereas in update=":checkTable, :messages" since we want to update the data table, we pass checkTable as argument and in order to display a 'checks removed' message, we also pass :messages to update attribute.
The important points in this section are as follows:
- For confirm dialog, we have two parts:
<p:confirm header="Confirmation" message="Do you want to delete this record?" icon="pi pi-info-circle"/>This will create the dialog box.
Second part is:
<h:form>
<p:confirmDialog global="true" responsive="true" width="350">
<p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no ui-button-flat"/>
<p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" />
</p:confirmDialog>
</h:form>This creates the options of yes or no.
Attention: This part must be written inside a HTML form.
The important points in this section are as follows:
- In order to make a modal dialog, the complete h:form id="checkForm" is now under a dialog tag.
<p:dialog widgetVar="checkDialog" closeOnEscape="true" modal="true" header="check">
<h:form id="checkForm">
<p:focus />
<p:panelGrid columns="2">
name:
<p:inputText value="#{checkListController.check.name}"/>
url:
<p:inputText value="#{checkListController.check.url}"/>
</p:panelGrid>
<h:commandButton value="save" action="#{checkListController.save()}" update="checkForm, :checkTable, :messages"/>
</h:form>
</p:dialog>Here, we also need to create a button which executes this prime faces modal dialog via java script.
<h:form>
<p:commandButton value="add check" oncomplete="PF('checkDialog').show()" />
</h:form>Here, the important part is oncomplete="PF('checkDialog').show()" which is executed once a Ajax request is send and response is acquired.
Attention: checkDialog passed an argument to PF() must be same as widgetVar in p:dialog widgetVar="checkDialog".
- In order to make edit button also open the modal dialog, we added oncomplete="PF('checkDialog').show()" to the edit button.
<p:commandButton value="edit" action="#{checkListController.setCheck(check)}" update=":checkForm" oncomplete="PF('checkDialog').show()"/>- In order to have a clean modal dialog for adding checks after an edit is performed, we need to do the following:
<h:form>
<p:commandButton value="add check" action="#{checkListController.clear()}"
update=":checkForm"
oncomplete="PF('checkDialog').show()" />
</h:form>We call the clear() method from the controller in action, we call update=":checkForm" so that the form is reloaded and finally, we call oncomplete="PF('checkDialog').show()" to display the modal dialog.
- Java 11
- PrimeFaces: PrimeFaces is a popular open source framework for JavaServer Faces featuring over 100 components, touch optimized mobilekit, client side validation, theme engine and more.
- SpringBoot: Used to create easy stand-alone, production-grade Spring based Applications.
- Maven: Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information.
- HikariCP: HikariCP is solid high-performance JDBC connection pool. A connection pool is a cache of database connections maintained so that the connections can be reused when future requests to the database are required. Connection pools may significantly reduce the overall resource usage.
- HSQLDB: HSQLDB (Hyper SQL Database) is a relational database management system written in Java. It has a JDBC driver and supports a large subset of SQL-92, SQL:2008, SQL:2011, and SQL:2016 standards. It offers a fast, small (around 1300 kilobytes in version 2.2) database engine which offers both in-memory and disk-based tables. Both embedded and server modes are available.
- Jetty server needs to be configured so that the welcome page is displayed correctly. How to configure this is defined above.
- To run the project, navigate to the project folder(where pom.xml is located) and execute the following command:
mvn jetty:runOnce the project is build successfully, please navigate to the following link:
http://localhost:8080/Here, one can see a default data set(containing 3 entries) already located from the database which is loaded to the GUI on application startup using @PostConstruct. Here, one can perform all CRUD(Create, Read, Update and Delete) operations.
Feature requests, issues, pull requests and questions are welcome.
Current:
- 1: JSF & PrimeFaces & Spring tutorial**(Primary resource)**
- 2: Youtube series
- 3: PrimeFaces showcase
- 4: Using HikariCP connection pool
- 5: HyperSQL Documentation
- 6: This fixed the compilation problem with lombok.
- 7: This explains the problem with @PostConstrcut annotation not being recognized. (Stack Overflow)
- 8: How to avoid hibernate problem with the creation of entitymanagerfactory
- 9: JSF Bean Scope & Description
How to reach me? At github specific gmail account.