I'm have obtained the following certifications to date:

  • Sun Certified Developer for Java Web Services 5
  • Sun Certified Specialist for NetBeans IDE
  • Sun Certified Solaris 10 System Administrator
  • Sun Certified Developer for Java 2 platform
  • Sun Certified Enterprise Architect for the Java Platform, Enterprise Edition 5
  • Sun Certified Programmer for Java 2 platform 1.4
  • Sun Certified Enterprise Architect for J2EE 1.4 Technology
  • IBM Certified SOA Solution Designer
  • IBM Certified Solution Designer – WebSphere MQ V5.3
  • IBM Certified Deployment Professional – WebSphere Studio Application Monitor V3.2
  • IBM Certified Application Developer – Rational Application Developer for WebSphere V6
  • IBM Certified Advanced System Administrator – WebSphere Application Server 5.0
  • IBM Certified System Administrator – WebSphere Application Server 5.0
Christopher has posted 2 posts at DZone. View Full User Profile

How to Create a Java EE 6 Application with JSF 2, EJB 3.1, JPA, and NetBeans IDE 6.8

12.29.2009
| 554906 views |
  • submit to reddit

Creating the Enterprise Java Beans (Session & Message-Driven)

Now that we have the Entity classes, the next task is to create the Session (Stateless) Bean, CustomerSession that will manipulate and provide the RU functionalities on the Customer objects. In this demo, the client that uses these functions are the JSF pages. One of the benefits of doing this (i.e. to provide the functionalities in the EJB layer) is reusability because the same functions can be used by more than one JSF pages, other EJBs, Enterprise Application Clients and Web Services Clients when exposed as web services. Other benefits include scalability because the EJB container can easily be tuned and scaled up when load increases.

We will also create a Message-Driven Bean (MDB), NotificationBean here to demonstrate its use for asynchronous messaging. In this tutorial, we want to send out notifications upon a successful update of a Customer record. For simplicity, we are just sending the updated Customer object to the Queue, so that the MDB can pick it up and process it in a separate thread.


  1. In the Projects window, right-click on the CustomerApp-ejb project and select "New > Session Bean..."

  2. In the New Session Bean dialog, specify the EJB Name as CustomerSession, the package as "com.customerapp.ejb", Session Type as Stateless and leave Create Interface unchecked, i.e. no Interface (New in EJB 3.1), and click Finish.


  3. In the Projects window again, right-click on the CustomerApp-ejb project and select "New > Message-Driven Bean..."

  4. In the New Message-Driven Bean dialog, specify the EJB Name as NotificationBean and the package as "com.customerapp.mdb", click on the "Add..." button of the Project Destination option.

  5. In the Add Message Destination dialog, specify the Destination Name as NotificationQueue and keep the Destination Type as Queue and click OK.


    Click Finish to complete the creation of the MDB.

  6. From the Projects window, navigate to the source of the newly created Session Bean (skeleton) by double clicking on the CustomerSessionBean item:


  7. In the code editor, right-click and select "Persistence > Use Entity Manager"; the "@PersistenceContext" notation is inserted automatically, so now the EntityManager, with variable name, em is ready to be used.

  8. Create the business methods for the Session Bean: Retrieve and Update; right-click in the code editor, select Insert Code..., under the Generate list, select Add Business Method...

  9. In the Add Business Method... dialog, provide the Name, Return Type and Parameters for the method:


  10. Repeat the steps for the Update function and notice the skeletons created for the 2 methods.

  11. Edit the methods so that they implement the intended functions as shown below:
    @Stateless
    @LocalBean
    public class CustomerSessionBean
    {
    @PersistenceContext
    private EntityManager em;

    /**
    * Returns a list of Customer objects in the database
    * @return List<Customer>
    */
    public List<Customer> retrieve()
    {
    Query query = em.createNamedQuery("Customer.findAll");
    return query.getResultList();
    }

    /**
    * Update the customer record
    * @param customer object to be updated
    * @return Customer
    */
    public Customer update(Customer customer)
    {
    return em.merge(customer);
    }
    }
    For the purpose of the demo, we will send the updated Customer object to the destination queue.

  12. In the code editor, right-click and select "Insert Code...", and "Send JMS Message" from the Generate list.

  13. In the Send JMS Message dialog, select "jms/NotificationQueue" as the Project Destination and click OK.

    *Notice the 2 auto-generated methods, createJMSMessageForjmsNotificationQueue and sendJMSMessageToNotificationQueue.

  14. In the update method, insert code to call the sendJMSMessageToNotificationQueue after the em.merge() statement and modify the createJMSMessageForjmsNotificationQueue method to send ObjectMessage instead:
    @Resource(name = "jms/NotificationQueue")
    private Queue notificationQueue;
    @Resource(name = "jms/NotificationQueueFactory")
    private ConnectionFactory notificationQueueFactory;

    public Customer update(Customer customer)
    {
    Customer updated = em.merge(customer);
    try
    {
    sendJMSMessageToNotificationQueue(updated);
    }
    catch (JMSException ex)
    {
    Logger.getLogger(CustomerSessionBean.class.getName()).log(Level.SEVERE, null, ex);
    }
    System.out.println("Customer updated in CustomerSessionBean!");
    return updated;
    }

    private Message createJMSMessageForjmsNotificationQueue(Session session, Object messageData) throws JMSException
    {
    //Modified to use ObjectMessage instead
    ObjectMessage tm = session.createObjectMessage();
    tm.setObject((Serializable) messageData);
    return tm;
    }

    private void sendJMSMessageToNotificationQueue(Object messageData) throws JMSException
    {
    Connection connection = null;
    Session session = null;
    try
    {
    connection = notificationQueueFactory.createConnection();
    session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    MessageProducer messageProducer = session.createProducer(notificationQueue);
    messageProducer.send(createJMSMessageForjmsNotificationQueue(session, messageData));
    }
    finally
    {
    if (session != null)
    {
    try
    {
    session.close();
    }
    catch (JMSException e)
    {
    Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot close session", e);
    }
    }
    if (connection != null)
    {
    connection.close();
    }
    }
    }
  15. In the Message-Driven Bean, NotificationBean, edit the onMessage method to simply print the details of the updated Customer object to show that it receives the message.

    Note that typically you would not want to process the message in the MDB itself: for best practices, the MDB should invoke methods in another Session Bean to perform the processing. But, for purposes of demonstration in this tutorial, we are stopping at the MDB:
    public void onMessage(Message message)
    {
    try
    {
    Object msgObj = ((ObjectMessage)message).getObject();
    if (msgObj != null)
    {
    Customer customer = (Customer)msgObj;
    System.out.println("Customer with the following details has been updated:");
    StringBuilder sb = new StringBuilder();
    sb.append("Customer ID=");
    sb.append(customer.getCustomerId());
    sb.append(", ");
    sb.append("Name=");
    sb.append(customer.getName());
    sb.append(", ");
    sb.append("Email=");
    sb.append(customer.getEmail());
    System.out.println(sb.toString());
    }
    }
    catch (JMSException ex)
    {
    Logger.getLogger(NotificationBean.class.getName()).log(Level.SEVERE, null, ex);
    }
    }
Up to this point, the tasks required to be done in the EJB project are completed, we will move on to the next tier, Presentation, i.e. the JSF pages.

Published at DZone with permission of its author, Christopher Lam.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Sreedhar Sudha replied on Wed, 2009/12/30 - 12:22am

Hi The working application zip file is corrupted. Could you please upload the correct one.

Dhiraj Thakur replied on Wed, 2009/12/30 - 2:04am

@sreedhar637 where is application.zip file? I am not able to find any attachment with this article.

 

Sreedhar Sudha replied on Wed, 2009/12/30 - 2:22am

In Summary page, author has given the link for the zip file

Cagatay Civici replied on Wed, 2009/12/30 - 5:16am

Thanks for the tutorial, this has been add to PrimeFaces external articles wiki.

http://code.google.com/p/primefaces/wiki/ExternalArticles

Slim Ouertani replied on Wed, 2009/12/30 - 7:34am

I want to not to hot deployemnt of glassfish V3 and deploy and save too.

thanks for this tuto.

Geertjan Wielenga replied on Wed, 2009/12/30 - 9:38am

Download link works fine for me:

http://netbeans.dzone.com/sites/all/files/CustomerApp_PrimeFaces.zip

Goutham Rao replied on Wed, 2009/12/30 - 10:18am

thanks for the great tutorial. For other users please add the Glassfish Java EE 6 library to CustomerApp-war to get imports for @EJB etc

Christopher Lam replied on Wed, 2009/12/30 - 11:47am

I created the zip file on my Mac, I thought that created some problem for the non-Mac users, but I tried setting it up from scratch on my Windows XP virtual machine (Virtualbox), and it seems fine. But I notice there are 2 things you need to do before opening the sample project:
  1. Turn on the "Java Web and EE" feature; either from the Plugins Manager or simply ensure the Glassfish v3 can be started
  2. Create the Global library for PrimeFaces as mentioned in the article
The above was all that I have to do on a new installation of NB6.8 (on WinXP) before unzipping and opening the project. Let me know if you still encounter problem.

Stephan Bardubitzki replied on Wed, 2009/12/30 - 12:07pm in response to: Cagatay Civici

PrimeFaces rockes! It will be my first choice of replacing Woodstock and even some Java ME projects.

Manuel Jordan replied on Thu, 2009/12/31 - 9:10am

Hello Christopher

 Excellent work!, thanks for share with DZone 

Best Regards

-Manuel

 

 

Henk De Boer replied on Fri, 2010/01/01 - 9:42am

Step 8 of page 5:
Lastly, we need to add the following statements in the web.xml file of the Web project for PrimeFaces to work correctly:

This wouldn't be needed if primefaces took advantage of the Servlet 3.0 annotations or embedded web.xml fragments.

Cagatay Civici replied on Fri, 2010/01/01 - 7:19pm

Henk, Upcoming PrimeFaces 2.0 release takes advantage of JSF 2.0's resource loading features so there's absolutely no need to configure anything with the new release.

Jose Alvarez de Lara replied on Sat, 2010/01/02 - 12:40am

Hi Christopher,

I have made my own version of your application: EJB 3.0, JSF 1.2, NB 6.7.1, etc...

And I have got an unusual exception as follows,

Parse Error at line 5 column 14: Document root element "faces-config", must match DOCTYPE root "null".
org.xml.sax.SAXParseException: Document root element "faces-config", must match DOCTYPE root "null".

I think it is a conflict of versions beetwen JSF 1.2 and PrimFaces for JSF 1.2, but I am not sure about it. The line and column the exception refers is,

 <faces-config version="1.2

On the other hand I am having problems with the GUI interface to develop the JSF pages. I do not see in my panel the elements you use to develop them. So may be an installation error and that is wat is happening.

What is going wrong?

 Thanks and Best Regards,

Jose

 

Jose Alvarez de Lara replied on Sat, 2010/01/02 - 5:19am in response to: Jose Alvarez de Lara

Hi Christopher,

I have fixed the issue. It was a wrong configuration of the application. But now I can not start up the application because I get another exception,

Initializing Sun's JavaServer Faces implementation (1.2_04-b20-p03) for context '/CustomerApp-war'
WebModule[/CustomerApp-war]PWC1275: Exception sending context initialized event to listener instance of class com.sun.faces.config.ConfigureListener
javax.faces.FacesException: Can't parse configuration file: jndi:/server/CustomerApp-war/WEB-INF/faces-config.xml: Error at line 12 column 47: cvc-pattern-valid: Value 'DETAILS' is not facet-valid with respect to pattern '#\{.*\}' for type 'faces-config-el-expressionType'.
        at com.sun.faces.config.ConfigureListener.parse(ConfigureListener.java:1438)
        at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:376)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4523)
        at org.apache.catalina.core.StandardContext.start(StandardContext.java:5184)
        at com.sun.enterprise.web.WebModule.start(WebModule.java:326)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:973)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:957)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:688)
        at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1584)
        at com.sun.enterprise.web.WebContainer.loadWebModule(WebContainer.java:1222)
        at com.sun.enterprise.web.WebContainer.loadJ2EEApplicationWebModules(WebContainer.java:1147)
        at com.sun.enterprise.server.TomcatApplicationLoader.doLoad(TomcatApplicationLoader.java:141)
        at com.sun.enterprise.server.AbstractLoader.load(AbstractLoader.java:244)
        at com.sun.enterprise.server.ApplicationManager.applicationDeployed(ApplicationManager.java:336)
        at com.sun.enterprise.server.ApplicationManager.applicationDeployed(ApplicationManager.java:210)
        at com.sun.enterprise.server.ApplicationManager.applicationDeployed(ApplicationManager.java:645)
        at com.sun.enterprise.admin.event.AdminEventMulticaster.invokeApplicationDeployEventListener(AdminEventMulticaster.java:928)
        at com.sun.enterprise.admin.event.AdminEventMulticaster.handleApplicationDeployEvent(AdminEventMulticaster.java:912)
        at com.sun.enterprise.admin.event.AdminEventMulticaster.processEvent(AdminEventMulticaster.java:461)
        at com.sun.enterprise.admin.event.AdminEventMulticaster.multicastEvent(AdminEventMulticaster.java:176)
        at com.sun.enterprise.admin.server.core.DeploymentNotificationHelper.multicastEvent(DeploymentNotificationHelper.java:308)
        at com.sun.enterprise.deployment.phasing.DeploymentServiceUtils.multicastEvent(DeploymentServiceUtils.java:226)
        at com.sun.enterprise.deployment.phasing.ServerDeploymentTarget.sendStartEvent(ServerDeploymentTarget.java:298)
        at com.sun.enterprise.deployment.phasing.ApplicationStartPhase.runPhase(ApplicationStartPhase.java:132)
        at com.sun.enterprise.deployment.phasing.DeploymentPhase.executePhase(DeploymentPhase.java:108)
        at com.sun.enterprise.deployment.phasing.PEDeploymentService.executePhases(PEDeploymentService.java:919)
        at com.sun.enterprise.deployment.phasing.PEDeploymentService.start(PEDeploymentService.java:591)
        at com.sun.enterprise.deployment.phasing.PEDeploymentService.start(PEDeploymentService.java:635)
        at com.sun.enterprise.admin.mbeans.ApplicationsConfigMBean.start(ApplicationsConfigMBean.java:744)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at com.sun.enterprise.admin.MBeanHelper.invokeOperationInBean(MBeanHelper.java:375)
        at com.sun.enterprise.admin.MBeanHelper.invokeOperationInBean(MBeanHelper.java:358)
        at com.sun.enterprise.admin.config.BaseConfigMBean.invoke(BaseConfigMBean.java:464)
        at com.sun.jmx.mbeanserver.DynamicMetaDataImpl.invoke(DynamicMetaDataImpl.java:213)
        at com.sun.jmx.mbeanserver.MetaDataImpl.invoke(MetaDataImpl.java:220)
        at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:815)
        at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:784)
        at sun.reflect.GeneratedMethodAccessor17.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:585)
        at com.sun.enterprise.admin.util.proxy.ProxyClass.invoke(ProxyClass.java:90)
        at $Proxy1.invoke(Unknown Source)
        at com.sun.enterprise.admin.server.core.jmx.SunoneInterceptor.invoke(SunoneInterceptor.java:304)
        at com.sun.enterprise.interceptor.DynamicInterceptor.invoke(DynamicInterceptor.java:174)
        at com.sun.enterprise.admin.jmx.remote.server.callers.InvokeCaller.call(InvokeCaller.java:69)
        at com.sun.enterprise.admin.jmx.remote.server.MBeanServerRequestHandler.handle(MBeanServerRequestHandler.java:155)
        at com.sun.enterprise.admin.jmx.remote.server.servlet.RemoteJmxConnectorServlet.processRequest(RemoteJmxConnectorServlet.java:122)
        at com.sun.enterprise.admin.jmx.remote.server.servlet.RemoteJmxConnectorServlet.doPost(RemoteJmxConnectorServlet.java:193)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:738)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
        at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:411)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:290)
        at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:271)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:202)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:94)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:206)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:571)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1080)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:150)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:632)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:577)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:571)
        at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:1080)
        at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:272)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.invokeAdapter(DefaultProcessorTask.java:637)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.doProcess(DefaultProcessorTask.java:568)
        at com.sun.enterprise.web.connector.grizzly.DefaultProcessorTask.process(DefaultProcessorTask.java:813)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.executeProcessorTask(DefaultReadTask.java:341)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:263)
        at com.sun.enterprise.web.connector.grizzly.DefaultReadTask.doTask(DefaultReadTask.java:214)
        at com.sun.enterprise.web.connector.grizzly.TaskBase.run(TaskBase.java:265)
        at com.sun.enterprise.web.connector.grizzly.WorkerThreadImpl.run(WorkerThreadImpl.java:116)
 

I have been googling and it seems there is a bug about this point. I would like you give me your opinion and if the issue can be fixed. I forgot to say that I am using glassfish v2ur2.

 

Thanking in advance,

Jose

Dario replied on Mon, 2010/01/04 - 7:34am

thanks for the great tutorial!. I've noticed that the retrive() method of CustomerSession is called 6 times when invoked from http://localhost:8080/CustomerApp-war/CustomerList.jsf, could you explain why ?

Henk De Boer replied on Mon, 2010/01/04 - 1:16pm in response to: Dario

thanks for the great tutorial!. I've noticed that the retrive() method of CustomerSession is called 6 times when invoked from http://localhost:8080/CustomerApp-war/CustomerList.jsf, could you explain why ?

It seems the author had made a basic JSF simplicity error here. Note that the data table binds to the CustomerMBean.customers method. This method on its turn directly invokes the session bean's method.

However, in JSF the component tree is processed in multiple phases, and EL references are resolved multiple times. This is simple because the data is needed multiple times. The simple solution is to just cache the retrieved list temporarily in an instance variable.

E.g.


public List getCustomers() {
   if (customers != null) {
      customers = customerSessionBean.retrieve();
   }
   return customers;
}

This pattern is so pervasive in JSF code that I'm surprised JSF hasn't provided a solution for this (like a @Retain annotation or something).

Be aware that you can't really apply the above presented solution to the code as-is. Namely, the author has chosen to put the backing bean in session scope, which is actually not really recommended. Many things might go wrong if the user happens to open the same page twice at the same time (and in practice, they actually do that a lot).

Another classical basic mistake the author seemed to have made here is binding the datatable directly to the customers list, without any guarantee that the data is still the same when the postback happens (i.e. when the customer ID link is clicked to invoke the details action).

If the data in the database has changed between calls, then the CustomerSessionBean.retrieve method will return different data. At least in the JSF 1.2 standard datatable, a click on a row is matched on row number. So, if the list contains different data (or the same data, but ordered differently), you'll click on one customer, but get to see the details of a totally different customer. Unless of course either JSF 2.0 or PrimeFaces retains the list automatically in view state. I'll have to test whether this indeed happens or not. If you however see that after a postback and before the invocation of the details action method the CustomerSessionBean.retrieve is called again, then this automatic retainment most likely doesn't happen.

Henk De Boer replied on Mon, 2010/01/04 - 1:41pm in response to: Henk De Boer

Many things might go wrong if the user happens to open the same page twice at the same time (and in practice, they actually do that a lot).

This actually happens since the backing bean stored the selected customer in an instance variable in the backing bean:


public class CustomerMBean {

     private Customer customer;

Just run the example app, click on some customer. Now open a new table or window, go to the list again and select another customer. Go back to the first tab or window and save the customer.

BAM!

You've just overwritten the second customer with all the data of the first customer that you didn't choose to render :(

The risk of a total overwrite is a little mitigated here, since the author also posts the ID of the customer back (which normally doesn't always happen) but it's still very much an anti-pattern. If the user would somehow posts two updates to each page in rapid succession, then you might end up with two entities in the DB that are a mix of each other.

Gavin King talked about this a LONG time ago, and specifically for this SEAM introduced the conversation scope. JSF 2.0 in Java EE 6 inherits this via CDI managed beans.

Christopher Lam replied on Mon, 2010/01/04 - 10:06pm

Thanks for the feedback.

The web tier is simplified by using only one Managed Bean, CustomerMBean, which has a scope of Session, and all the properties are provided there. I admit that this is over simplified, esp. so when the life-cycle of JSF causes it to call the method several times when actually one is enough. Caching the customer list in CustomerMBean will also pose a problem because then the list will never get updated throughout the session.

So the right thing to do in this case is to create a new Managed Bean, I called it CustomerListMBean and scope it as View or Request, then provide the getCustomers() method there with the list cached as follows:
    private List<Customer> custList = null;

    public List<Customer> getCustomers()
    {
        if (custList == null)
        {
            custList = customerSessionBean.retrieve();
        }
        return custList;
    }

Because this new bean only survives during the View or the Request, the caching will not pose a problem. Hope this helps.

Christopher Lam

Henk De Boer replied on Tue, 2010/01/05 - 4:55am in response to: Christopher Lam

Hope this helps.

It's a start, but now you have to think about how to restore the entity bean that you wanted updated after the post-back from the detail view. Since you post-back the ID, one option would be to initiate the instance variable in the backing bean with a new Customer(). Then upon saving, retrieve the original Customer again from the DB, copy over all attributes and save the result.

Alternatively, you could store the selected Customer in conversation scope. JSF 2.0 has facilities for that. In JSF 1.2, one would typically use the tomahawk savestate tag for this (or one of the few other extensions).

Unfortunately, you might still not have solved the datamodel problem (=the list attached to your datatable). To test this: first let your view be rendered. Now, while this view sits on your screen, quickly go the DB and add some data to the beginning. Go back to your screen, and click on a Customer after the one you've just added to the DB (e.g. if you added a Customer with ID 4 and the returned list is ordered on ID, click on Customer 5 or higher).

Did you get the right Customer? Probably not. If you click on Customer 5, I predict you'll get to see the details of Customer 4.

A small additional thing is that your example also suffers from the one-URL-behind problem. You post to the list view and because of the navigation rules without any redirect, the detail view is shown on screen. However, the address bar still clearly shows the list URL. This is highly confusing for users. In JSF 1.2, you can circumvent this problem by using a redirect directive in your navigation rules, but if you do that you loose your state.

Now it would be very interesting to see whether JSF 2.0 has a solution for this, i.e. by using the conversation scope and maybe the improved navigation rules. The JSF 1.2 solution for this was doing a programmatic redirect, in which you could supply a GET parameter (like the ID of the chosen Customer).

Anonymougfgdfdgs (not verified) replied on Wed, 2010/01/06 - 5:51am

The risk of a total overwrite is a little mitigated here, since the author also posts the ID of the customer back (which normally doesn't always happen) but it's still very much an anti-pattern. If the user would somehow posts two updates to each page in rapid succession, then you might end up with two entities in the DB that are a mix of each other. Gavin King talked about this a LONG time ago, and specifically for this SEAM introduced the conversation scope. JSF 2.0 in Java EE 6 inherits this via CDI managed beans.

Henk De Boer replied on Wed, 2010/01/06 - 1:49pm in response to:

JSF 2.0 in Java EE 6 inherits this via CDI managed beans.

Indeed, I think the author should have used these here. Not only are they actually very much to the point here, this is also supposed to be a Java EE 6 example. Currently it happens the Java EE 5/JSF 1.2 way, and the not entirely right JSF 1.2 way that is.

Although I greatly appreciate the effort the author poured into this article, with all the screen shots and such, it's a petty that he doesn't seem to understand this whole communication between pages and postbacks in the web layer really well.

This is understandable, since it's one of the most frequently misunderstood concepts in JSF (and web frameworks in general really), but a wrong example only causes more confusion with people new to JSF.

Mrh Phreak replied on Thu, 2010/01/14 - 5:31am

Thank you Christopher for this great tutorial. Also thanks to those who read this tutorial and provided their opinions, especially Henk De Boer for his constructive criticism. I learned a lot from from this discussion.

Karl Krasnowsky replied on Sun, 2010/01/24 - 4:01pm

Christopher et al,

I started using this tutorial and hit a snag using the JavaDB sample database right out of the gate.

The contents of my sample DB does not match that of the tutorial. Could someone supply a script so I can create the Customer table and applicable data?

thanks!

 

Follow up... 1/24:

Nevermind, not that anyone responding anyway. Turns out a reinstall of Netbeans provided the sample derby database as mentioned.

Karl Krasnowsky replied on Sun, 2010/01/24 - 6:26pm

Niggly.

#2 Dialog of section that describes the creation of the system beans instructs to create the CustomerSessionBean as simply CustomerSession. It's referred to as it's former name throughout the rest of the tutorial.

Tom Laverriere replied on Fri, 2010/02/19 - 5:40pm

I have been trying to deploy this application on various builds of Glassfish v3 and keep getting the following error.  Any help is very much appreciated:

org.glassfish.deployment.common.DeploymentException: Message Driven Beans can't be Managed Beans
        at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:155)
        at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:88)
        at org.glassfish.internal.data.ApplicationInfo.load(ApplicationInfo.java:224)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:338)
        at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:183)
        at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:272)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$1.execute(CommandRunnerImpl.java:305)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:320)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1176)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$900(CommandRunnerImpl.java:83)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1235)
        at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1224)
        at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:365)
        at com.sun.enterprise.v3.admin.AdminAdapter.service(AdminAdapter.java:204)
        at com.sun.grizzly.tcp.http11.GrizzlyAdapter.service(GrizzlyAdapter.java:166)
        at com.sun.enterprise.v3.server.HK2Dispatcher.dispath(HK2Dispatcher.java:100)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:245)
        at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
        at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
        at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
        at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:168)
        at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
        at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
        at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
        at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
        at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
        at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
        at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
        at java.lang.Thread.run(Thread.java:619)
Caused by: org.jboss.weld.DefinitionException: Message Driven Beans can't be Managed Beans
        at org.jboss.weld.bean.SessionBean.checkEJBTypeAllowed(SessionBean.java:313)
        at org.jboss.weld.bean.SessionBean.initialize(SessionBean.java:122)
        at org.jboss.weld.bootstrap.AbstractBeanDeployer.deploy(AbstractBeanDeployer.java:111)
        at org.jboss.weld.bootstrap.BeanDeployment.deployBeans(BeanDeployment.java:151)
        at org.jboss.weld.bootstrap.WeldBootstrap.deployBeans(WeldBootstrap.java:367)
        at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:153)

Righardt Swanepoel replied on Fri, 2010/03/26 - 4:17am

Hi, Anybody else having a 'log in' screen pop up when you try to edit content of the table? Sorry if this is a stupid question, but I'm clearly missing something. :) Thanks in advance for any advise!

Righardt Swanepoel replied on Fri, 2010/03/26 - 4:36am

Situation resolved. I didn't change my xhtml template to the suggested format. It's all good now. BTW I would also like to thank Christopher for his tutorial, as well as every one else for their comments. I learnt a lot!

Rudy Hadoux replied on Sun, 2010/03/28 - 4:37am

Hi everybody, I encountered many problems for creating this very interesting tutorial. If anyone could help me it would be great. the listing :

1. the archive of Christopher does not work for me. Is it a EAR archive ? I have to rename this archive ?

2. I can not replace my xhtml template by Christopher's one.

${encoding} is not accepted.
${project.license} is not accepted.
xmlns:f="http://java.sun.com/jsf/core" is not accepted.
xmlns:h="http://java.sun.com/jsf/html" is not accepted.
xmlns:p="http://primefaces.prime.com.tr/ui" is not accepted.

3. In the Projects window, right-click on the Web project, CustomerApp-war, and select "New > JSF Managed Bean...", specify CustomerMBean as the Class Name, "com.customerapp.web" as the Package Name, customer as the Name, and the scope to be session. ==> I can not add data to configuration file.

It is maybe a problem with jsf and primerfaces libraries but I put it in my application. So, I do not see what it is wrong ?

Very grateful for your possible answer.

Rudy.

_Jon T. replied on Thu, 2010/04/29 - 10:42am

 

With regard to the "Message Driven Beans can't be Managed Beans" error, I had to remove the 'beans.xml' files from the application folders.

Prashanth Kumar replied on Fri, 2010/05/21 - 6:04pm

Hi, download the zip file from the summary page. however i am not able to unzip the file.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.