Click here to Skip to main content
Click here to Skip to main content

J2EE - NetBeans, JSF, Persistence API - Part 3

, 1 Apr 2013
Rate this:
Please Sign up or sign in to vote.
How to extend the detail page of a company by adding a list of products of the company and a drop down list box, which will allow a user to add a new product to the list of the products of the company.

This blog entry is a continuation of a series in which I try to show how I taught myself the basics of J2EE. You can see the first part and the second part.

The aim of this part is to extend the detail page of a company by adding a list of products of the company and a drop down list box, which will allow a user to add a new product to the list of the products of the company. Here is what it should look like in the end.

Generally adding the list of the products of the company to the page will be a piece of cake. You can again use the dataTable component provided by JSF. What will be a bit tricky is adding a drop down list. We will use the selectOnListBox component populated by List<product>. Because this component allows to show String value and as I seed we will put in Product class. We will have to use a Converter component which will translate the Product to a single String and vice versa.

Changes to the Session Bean

Again, here I will start with the changes in the lower level. We will need to add a method which will provide all the products in the database as a List object. Later, we will populate a drop down list with these products.

public List<product> getAllProducts() {
    List<product> products = 
	em.createNamedQuery("Product.getAllProducts").getResultList();
    return products;
}

This method is calling a named query which you need to place above the "Product" class declaration.

@Entity
@NamedQuery(
    name="Product.getAllProducts",
    query="SELECT p FROM Product p"
)
public class Product implements Serializable {

}

Then we will need a method which will find a product for a given ID. We can easily use a method of the Entity Manager which allows to find an object by a given ID (the ID field is decorated as an identity in the Entity Beans declaration. This method will be used by the Converter class.

public Product getProductWithId(String id) {
    try {
        return em.find(Product.class, Integer.parseInt(id));
    } catch (java.lang.NumberFormatException e) {
        e.printStackTrace();
    }
    return null;
}

The Converter Class

As it had been said already, we will implement a class which allows us to convert an object of type Product to a String. This class has to implement the Converter interface provided by the JSF framework. This interface has two methods which have to be overridden: getAsObject and getAsString. Both of these methods have three arguments.

FacesContext specifies the context of the current request. FacesContext is an object which takes care of all the phases needed to render a response to the request. When a user demands a page, the user's request is first processed by the Faces Servlet. If the call is a postback of a previously viewed page, Faces Servlet will use the ID of the view and will look up the FacesContext of the page. If it is an initial view of the page, new FacesContext will be created. FacesContext connects the Component Tree of your page with the Backing Bean containing the logic. To get more information about the JSF life cycle and the FacesContext, visit this series. Now the second argument is the UI Component to which the Converter is connected and the 3rd argument is either String value or Object - depending on which way you are currently converting. Now - you just have to implement these methods, you will probably never call them in your code. Here is my ProductConverter class which I decided to put into package sales.util.

public class ProductConverter implements Converter{

    public Object getAsObject
	(FacesContext context, UIComponent component, String value) {
        SalesSessionLocal ssb = lookupSessionBean();
        Product p = ssb.getProductWithId(value);
        return p;
    }

    public String getAsString
	(FacesContext context, UIComponent component, Object value) {
        String id = String.valueOf(((Product)value).getId());
        return id;
    }

    private SalesSessionLocal lookupSessionBean(){
        try{
            Context c = new InitialContext();
            SalesSessionLocal ssb = (SalesSessionLocal) 
			c.lookup("java:comp/env/SalesSessionBean");
            return ssb;
        }catch(NamingException ex){
            return null;
        }
    }
}

The conversion of the object to the String is quite easy - you just need to decide what describes the object. Here you can see that I just return the products ID.
The conversion of a String to object is the processing of looking up the product of a given ID in our Entity Manager. We had prepared a method for this lookup in the Session Bean, which we are basically calling by the following line:

Product p = ssb.getProductWithId(value);

Now the question is how to obtain the Session Bean. Normally in the Backing Bean, we are using the EJB injection by the "@EJB" decoration. This means that we just write one single line and we obtain the Session Bean (no initialization, the framework takes care of this).

@EJB
SalesSessionLocal ssl;

However in the Converter, you are not able to use this approach. The injection is allowed just in Managed Beans (another name for Backing Beans). The solution here is the usage of JNDI Lookup. JNDI - in general is a standard JAVA interface of locating users, computers but also objects and services. We need to lookup an object, concretely Session Bean, which is a part of the same J2EE application. The details of this lookup can be found here. Generally when the application server is started, it creates for each application an InitialContext, which is an object that contains the Name - Object bindings. Using this InitialContext, we can obtain the desired object if we know the name of the object. We give the name to the object (Session Bean) when we register it in the web.xml configuration file. Here is the registration of EJB in web.xml.

<ejb-local-ref>
    <ejb-ref-name>SalesSessionBean</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <local>sales.SalesSessionLocal</local>
    <ejb-link>SalesManagement-ejb.jar#SalesSessionBean</ejb-link>
</ejb-local-ref>

And here is how we can use the JNDI Lookup to get the Session Bean in our GlassFish InitialContext.

Context c = new InitialContext();
            SalesSessionLocal ssb = (SalesSessionLocal) c.lookup
		("java:comp/env/SalesSessionBean");

Changing Faces Configuration

So that's it, the Converter class is ready to be used. Now we have to register it in the Faces Configuration; in the faces-config.xml file, add the following:

<converter>
        <description>Converts Product Entity to String</description>
        <converter-id>ProductConverter</converter-id>
        <converter-class>sales.util.ProductConverter</converter-class>
</converter>

Changes to the Managed Bean (another name for Backing Bean)

Now we will have to add some methods and fields to the Backing Bean to assure the functionality. First, we will need to provide a field (and of course setters and getters) which will store the actual selected "Product" in the drop down list.

private Product selectedProduct;
public Product getSelectedProduct() {
    return selectedProduct;
}

public void setSelectedProduct(Product selectedProduct) {
    this.selectedProduct = selectedProduct;
}

Now the next method called "addProduct" will just add the "selectedProduct" to the list of products of the current company.

public void addProduct(){
  if(this.company.getProducts() == null){
      this.company.setProducts(new ArrayList<product>());
  }
  this.company.getProducts().add(selectedProduct);
  ssl.saveCompany(company);
}

The latest method will return a collection of products in as a list of SelectItem, which will represent the collection of products in the drop down list. This method benefits from the previously created method for getting all products in the Session Bean.

public List<SelectItem> getAllProductsSelectList() {
    List<SelectItem> items = new ArrayList<SelectItem>();
    for (Product p : ssl.getAllProducts()) {
        items.add(new SelectItem(p, p.getId() + " " + p.getName()));
    }
    return items;
}

Using the Converter in the Web Page

Now you should be ready to use to converter in your web page. Open the page with the details of the company ("company.jsp" in my case), and add the following table of products of one company.

<h:datatable value="#{sales.company.products}" var="item">
       <h:column>
              <f:facet name="header"><h:outputtext value="Name" /></f:facet>
              <h:outputtext value="#{item.name}">
       </h:outputtext></h:column>
       <h:column>
              <f:facet name="header"><h:outputtext value="Description" /></f:facet>
              <h:outputtext value="#{item.description}">
       </h:outputtext></h:column>
</h:datatable>

That is basically the same as done before in part 1, just notice that the table is binded to the list of the products of the "Company" object in Backing Bean. Now the following piece of code actually adds the drop down list box, which allows the user to select one product and add it to the company.

<h3><h:outputtext value="Add Product:" /></h3>
       <h:selectonelistbox value="#{sales.selectedProduct}" size="1">
            <f:selectitems value="#{sales.allProductsSelectList}">
            <f:converter converterid="ProductConverter">
       </f:converter></f:selectitems></h:selectonelistbox>
<h:commandbutton id="btnAdd" action="#{sales.addProduct}" value="Add">
</h:commandbutton>

You can see that the selectOneListbox component is binded to the "selectedProduct" field defined in the Backing Bean as well as the items list is binded to the list of products in the Backing Bean. The component also contains a reference to the converter which should be used, which we have earlier prepared in the faces-config.xml. If you try this example right now, it will not work, instead you will obtain a strange validation error in your GlassFish log.
To be used by custom JSF converter, the entity class needs to override the Equals method. This is because the selected item is binded to the Backing Bean and when the selected item changes, it is compared to the binded field in the Backing Bean. So now, you have to override the equals method for your "Product" class. To distinguish two products, you can simply compare their IDs.

    @Override
    public boolean equals(Object obj){
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }

        final Product other = (Product) obj;
        if (this.getId() != other.getId()) {
            return false;
        }
        return true;
    }

To try out your example, you need some simple testing data. You can run this script (just check the names of the fields in the database tables before).

INSERT INTO SALES.COMPANY (companyname, companydescription) _
	values('First company', 'Sales bananas');
INSERT INTO SALES.COMPANY (companyname, companydescription) _
	values('Second company', 'Sales oranges');

INSERT INTO SALES.PRODUCT (productname,productdescription) _
	values ('Product 1','Orange');
INSERT INTO SALES.PRODUCT (productname,productdescription) _
	values ('Product 2','Banana');

That's it for now.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Jan Fajfr
Software Developer (Junior) OCTO Technology
Czech Republic Czech Republic
I work as IT consultant at OCTO Technology. Previously I have lived and studied CS in Prague, Paris and Valencia.
 
LinkedIn
Blog
GitHub
Articles at OCTO blog

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140821.2 | Last Updated 2 Apr 2013
Article Copyright 2010 by Jan Fajfr
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid