4

I'm working on a JSF form prototype for inserting data into database table using AJAX data validation This is the JSF page:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"    
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <ui:insert name="header">           
            <ui:include src="header.xhtml"/>         
        </ui:insert>
    </h:head>
    <h:body>

        <h1><img src="resources/css/images/icon.png" alt="NVIDIA.com" /> History Center</h1>
        <!-- layer for black background of the buttons -->
        <div id="toolbar" style="margin: 0 auto; width:1180px; height:30px; position:relative;  background-color:black">
            <!-- Include page Navigation -->
            <ui:insert name="Navigation">           
                <ui:include src="Navigation.xhtml"/>         
            </ui:insert>

        </div>  

        <div id="logodiv" style="position:relative; top:35px; left:0px;"> 
            <h:graphicImage alt="Demo Insert Form"  style="position:relative; top:-20px; left:9px;"  value="resources/images/logo_databasez.png" />
        </div>
        <div id="main" style="margin: 0 auto; width:1190px; height:700px; position:absolute;  background-color:transparent; top:105px">

            <div id="mainpage" style="margin: 0 auto; width:1190px; height:500px; position:absolute;  background-color:transparent; top:80px">

                <div id="settingsHashMap" style="width:350px; height:400px; position:absolute;  background-color:r; top:20px; left:1px">
                    <h:form>
                        <div id="settingsdiv" style="width:750px; height:400px; position:absolute;  background-color:r; top:20px; left:1px">

                            <h:panelGrid columns="2">
                                <h:panelGroup>Session ID</h:panelGroup>
                                <h:panelGroup>
                                    <h:inputText id="sessionid" value="#{DatabaseController.formMap['sessionid']}" >
                                        <f:validateLength minimum="0" maximum="15"/>
                                        <f:ajax render="sessionidvalidate" event="blur"/>                                          
                                    </h:inputText>
                                    <h:outputText id="sessionidvalidate" rendered="#{DatabaseController.validateSessionid}" value="session is already registered" />
                                </h:panelGroup>

                                <h:panelGroup>User ID</h:panelGroup>
                                <h:panelGroup>
                                    <h:inputText id="userid" value="#{DatabaseController.formMap['userid']}" >
                                        <f:validateLength minimum="0" maximum="15"/>
                                    </h:inputText>
                                </h:panelGroup>

                                <h:panelGroup>Login Time</h:panelGroup>
                                <h:panelGroup>
                                    <h:inputText id="logintime" value="#{DatabaseController.formMap['logintime']}" >
                                        <f:validateLength minimum="0" maximum="35"/>
                                    </h:inputText>
                                </h:panelGroup>

                                <h:panelGroup>Last Refresh Time</h:panelGroup>
                                <h:panelGroup>
                                    <h:inputText id="lastrefreshtime" value="#{DatabaseController.formMap['lastrefreshtime']}" >
                                        <f:validateLength minimum="0" maximum="35"/>
                                    </h:inputText>
                                </h:panelGroup>

                                <h:panelGroup>User IP</h:panelGroup>
                                <h:panelGroup>
                                    <h:inputText id="userip" value="#{DatabaseController.formMap['userip']}" >
                                        <f:validateLength minimum="0" maximum="15"/>
                                    </h:inputText>
                                </h:panelGroup>

                            </h:panelGrid>          

                        </div>   

                        <div id="settingstwodiv" style="width:150px; height:60px; position:absolute;  background-color:transparent; top:380px; left:800px">

                            <h:commandButton value="Create User" action="#{DatabaseController.saveData}"/>

                        </div> 
                    </h:form> 

                </div>   

            </div>  
        </div>

    </h:body>
</html>

This is the managed bean:

import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
// or import javax.faces.bean.SessionScoped;
import javax.inject.Named;
/* include SQL Packages */
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import javax.annotation.Resource;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
// or import javax.faces.bean.ManagedBean;   

import org.glassfish.osgicdi.OSGiService;

// Demo Insert Form
@Named("DatabaseController")
@SessionScoped
public class Database implements Serializable {

    private HashMap<String, String> formMap = new HashMap<>();

    public Database() {
    }
    /* Call the Oracle JDBC Connection driver */
    @Resource(name = "jdbc/Oracle")
    private DataSource ds;

    public HashMap<String, String> getformMap() {
        return formMap;
    }

    /*

    CREATE TABLE ACTIVESESSIONS(
    SESSIONID VARCHAR2(30 ) NOT NULL,
    USERID VARCHAR2(30 ) NOT NULL,
    LOGINTIME TIMESTAMP(6),
    LASTREFRESHTIME TIMESTAMP(6),
    USERIP VARCHAR2(30 )
    )
    /
     */
    @PostConstruct
    public void hashMapGenerate() {
        /* Initialize the hashmap */
        formMap.put("sessionid", null);
        formMap.put("userid", null);
        formMap.put("logintime", null);
        formMap.put("lastrefreshtime", null);
        formMap.put("userip", null);
    }

    public int saveData() throws SQLException, java.text.ParseException {
//        formMap = new HashMap<String, String>();

        String SqlStatement = null;

        if (ds == null) {
            throw new SQLException();
        }

        Connection conn = ds.getConnection();
        if (conn == null) {
            throw new SQLException();
        }

        PreparedStatement ps = null;

        /*

        CREATE TABLE ACTIVESESSIONS(
        SESSIONID VARCHAR2(30 ) NOT NULL,
        USERID VARCHAR2(30 ) NOT NULL,
        LOGINTIME TIMESTAMP(6),
        LASTREFRESHTIME TIMESTAMP(6),
        USERIP VARCHAR2(30 )
        )
        /
         */

        try {
            conn.setAutoCommit(false);
            boolean committed = false;
            try {           /* insert into Oracle the default system(Linux) time */
                SqlStatement = "INSERT INTO ACTIVESESSIONS"
                        + " (SESSIONID, USERID, LOGINTIME, LASTREFRESHTIME, USERIP)"
                        + " VALUES (?, ?, ?, ?, ?)";

                ps = conn.prepareStatement(SqlStatement);

                ps.setString(1, formMap.get("sessionid"));
                ps.setString(2, formMap.get("userid"));
                ps.setTimestamp(3, toTimeStamp(formMap.get("logintime")));
                ps.setTimestamp(4, toTimeStamp(formMap.get("lastrefreshtime")));
                ps.setString(5, formMap.get("userip"));

                ps.executeUpdate();

                conn.commit();
                committed = true;
            }
            finally 
            {
                if (!committed) {
                    conn.rollback();
                }
            }
        } finally {
            /* Release the resources */
            ps.close();
            conn.close();
        }

        return 0;

    }
    private Timestamp toTimeStamp(String s) throws java.text.ParseException
    {
        Timestamp ts = null;
        java.util.Date date = null;
        if (s == null || s.trim().isEmpty()) return ts;

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.S");
        date = (java.util.Date) sdf.parse(s);
        ts = new Timestamp(date.getTime());

        return ts;

    }

    /* Validators section */
    public boolean getvalidateSessionid(){

        //do SQL query

        return true;
    }

}

I'm having difficulties in implementing the form validation. I want to pass the String entered by the used and check it into database is this value already into the database table. Here I'm calling the Java method to check the value:

                            <h:panelGroup>
                                <h:inputText id="sessionid" value="#{DatabaseController.formMap['sessionid']}" >
                                    <f:validateLength minimum="0" maximum="15"/>
                                    <f:ajax render="sessionidvalidate" event="blur"/>                                          
                                </h:inputText>
                                <h:outputText id="sessionidvalidate" rendered="#{DatabaseController.validateSessionid}" value="session is already registered" />
                            </h:panelGroup>

How I can send the inserted value to the Java method?

Best Wishes

Peter Penzov
  • 1,126
  • 134
  • 430
  • 808

2 Answers2

10

You need to create a Validator.

Kickoff example:

@FacesValidator("sessionIdValidator")
public class SessionIdValidator implements Validator {

    @Override
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        // ...

        if (yourDataService.existSessionId(value)) {
            throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
                "Session ID is already in use, please choose another.", null));
        }
    }

}

Use it as follows:

<h:inputText id="sessionid" value="#{DatabaseController.formMap['sessionid']}">
    <f:validateLength minimum="0" maximum="15" />
    <f:validator validatorId="sessionIdValidator" />
    <f:ajax event="blur" render="sessionidMessage" />                                          
</h:inputText>
<h:message id="sessionidMessage" for="sessionid" />

Note that you should use a <h:message> to show validation messages. You haven't any one in your view. Apply the same for all your other fields:

<h:inputText id="userid" value="#{DatabaseController.formMap['userid']}">
    <f:validateLength minimum="0" maximum="15" />
    <f:ajax event="blur" render="useridMessage" />                                          
</h:inputText>
<h:message id="useridMessage" for="userid" />

If you want to use @Resource or @EJB or @Inject in the validator, then replace the @FacesValidator annotation by @ManagedBean or (as you seem to use CDI for some reason) @Named:

@Named
public class SessionIdValidator implements Validator {

    @Resource
    private DataSource dataSource;

    @Inject
    private YourDataService yourDataService;

    // ...
}

and use it as follows instead

    <f:validator binding="#{sessionIdValidator}" />
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thank you for the example! If I implement your validation example when the error message will appear into the web page? When I press the button or when I enter anything? – Peter Penzov May 04 '12 at 20:03
  • 1
    According to the ``, only when the input field blurs. – BalusC May 04 '12 at 20:05
  • Blur means - (losing focus) events in the text input. I think that this means when I enter some data into the filed and I click outside the field the data is validated. How I can configure the input field to check every new letter entered into the input field? Maybe event="change"? – Peter Penzov May 04 '12 at 20:28
  • 1
    No, use `event="keyup"`. Keep in mind that this is rather expensive. The `event="change"` only fires when you blurs the field after you've changed the value. I was just using `event="blur"` because you already have it in your code snippet. Note that the `event="change"` does thus not fire when you don't change the value, but if you want to validate `required="true"` immediately on an empty field and so on, you'd better use `event="blur"`. – BalusC May 04 '12 at 20:33
  • Maybe if there is a way to activate event="keyup" only when there are entered more than for example 6 letters it will become less expensive? – Peter Penzov May 04 '12 at 20:36
  • One more question: Can I have one FacesValidator("sessionIdValidator") in separate java file but to be able to validate more than one field? As far as I see now I need for every field separate validator. – Peter Penzov May 05 '12 at 10:15
  • 1
    Yes, that's possible. You could use `` on the component to specify the "type" of the value. E.g.``. You can get it in the validator by `component.getAttributes().get("type")`. – BalusC May 05 '12 at 11:45
  • I found into the book "Core Java Server Faces" That I can create custom validator - page 290. Here is the code so far: http://pastebin.com/fN1KvYkC The question is when the string is validated how I can send the return message to the tag? – Peter Penzov May 05 '12 at 14:00
  • 1
    Implement `Validator` and throw `ValidatorException` with a `FacesMessage` as demonstrated in my answer. – BalusC May 05 '12 at 17:45
1

Just add listener in your tag h:inputText:

<h:inputText id="sessionid" value="#{DatabaseController.formMap['sessionid']}" 
         valueChangeListener="#{DatabaseController.validateSessionid}">

So you can check the entered value on the server side. When a user tabs out of the input field, JSF makes an ajax-call to the server and runs the name input component through the execute portion of the life cycle. This means that JSF will invoke the name input's value-change listener specified in attribute valueChangeListener during the validations process phase of the life cycle.

And I would suggest to implement this method:

public void validateSessionid(ValueChangeEvent e) {
    UIInput nameInput = e.getComponent()
    String sessionid = nameInput.getValue()

    //do SQL query
}

After the ajax-call returns JSF renders the h:outputText id="sessionidvalidate".

kapandron
  • 3,546
  • 2
  • 25
  • 38
  • 1
    The `valueChangeListener` serves an entirely different purpose. It's intented to listen on value change events, not to validate values. – BalusC May 04 '12 at 19:56
  • 1
    But the process in this case will be virtually the same as when using a custom validator. What is the fundamental difference? – kapandron May 04 '12 at 20:41
  • 1
    Using the right tool for the job. If you want to validate something, use a `Validator`. It's well suited for the job, it has a well definied `validate()` method wherein you can throw a `ValidatorException` which will properly force JSF to treat the submit as invalid and handle the message. In the `valueChangeListener` you'd have to do it all manually. – BalusC May 04 '12 at 20:52
  • 1
    But simple dynamic rendering of a error message is cheaper than the generating the exception in this situation, don't it? – kapandron May 04 '12 at 21:19
  • 1
    Using a `Validator` really won't take any second longer and it's well integrated in JSF framework and thus easier to (re)use. Also, don't forget to mark the component invalid, mark the faces context invalid, skip update model values and invoke application phases when validation has failed in your value change listener. – BalusC May 04 '12 at 21:23
  • Thank you for the answer. I was looking for another way of creating async requests instead of f:ajax. Your solution worked fine – bond_t Aug 13 '15 at 07:53