2 Replies Latest reply on Feb 5, 2009 11:48 AM by janscherf

    Refreshing Model objects after cancel

    nealhaggard

      Using the following technologies/versions



      • JBoss 4.2.2.GA

      • Seam 2.0.1.GA

      • RichFaces 3.2.0.SR1

      • Spring 2.0.8



      Problem description


      I'm working on a simple account creation/listing/editing/deleting tool, based on samples from the Seam Documentation and from other people on here.  For the most part it works as I expect, however I seem to be having problems with something that I'd consider a fairly simple thing to do, cancel an edit. 


      Right now if a user drills in to edit an account and makes a change but hits 'Cancel' instead of 'Save' on the edit page, it returns to the account list page but the changes made on the edit page show up in the account list.  Watching the logs, I don't see the SQL update happen, and if I examine the database or close my browser and come back in I see the original values are still there.


      Here's my cancel method:


              @End
              public void cancelEdit() {
                      if (selectedAccount != null) {
                              accountService.merge(selectedAccount);
                              accountService.refresh(selectedAccount);
                              accountService.flush();
                      }
                      accountsList();
              }
      



      As you can see I'm trying to re-merge the selected account back onto the session (as just doing 'refresh' was throwing an exception saying that the entity was not being tracked), before refreshing it (which properly generates SQL to reload it from the database) then flush the entity manager and reload the account list.


      I'm using Spring managed transactions and wrapping the Seam Entity Manager around my Spring entity manger.  I am trying to manually manage the flush-mode on my transaction for this conversation by annotating my edit method with


      @Begin(..., flushMode = FlushModeType.MANUAL)



      and manually calling flush when I call saveEdit() to end the conversation.


      Is there something I'm missing?  Why can't I get the account list to refresh and show the original values and not what Hibernate/JPA has in it's memory.


      Source Code



      Full Source Code is provided below.


      AccountListingBean.java


      package myapp.web.accounts;
      
      import java.io.Serializable;
      import java.util.List;
      
      import myapp.domain.account.AccountService;
      import myapp.framework.account.Account;
      import myapp.framework.util.Constants;
      import myapp.web.util.WebUtil;
      
      import javax.ejb.Remove;
      import javax.faces.application.FacesMessage;
      
      import org.jboss.seam.ScopeType;
      import org.jboss.seam.annotations.Begin;
      import org.jboss.seam.annotations.Create;
      import org.jboss.seam.annotations.End;
      import org.jboss.seam.annotations.Factory;
      import org.jboss.seam.annotations.FlushModeType;
      import org.jboss.seam.annotations.In;
      import org.jboss.seam.annotations.Name;
      import org.jboss.seam.annotations.Scope;
      import org.jboss.seam.annotations.datamodel.DataModel;
      import org.jboss.seam.annotations.datamodel.DataModelSelection;
      import org.jboss.seam.annotations.security.Restrict;
      import org.jboss.seam.faces.FacesMessages;
      
      /**
       * Seam implementation of an accounts listing bean.
       * 
       * @author Neal
       * @version $Revision: 1.3 $
       */
      @Name("accountListing")
      @Scope(ScopeType.SESSION)
      public class AccountsListBean implements AccountsListing, Serializable {
           private static final long serialVersionUID = 7808109730367613834L;
      
           @In(value = "#{accountService}")
           private AccountService accountService;
      
           @DataModel
           List<Account> accountsList = null;
      
           @DataModelSelection
           Account selectedAccount = null;
      
           // allow bookmark-able pages, respond to id
           Long accountId;
      
           @Factory("accountsList")
           public void accountsList() {
                accountsList = accountService.findAll();
           }
      
           @In
           FacesMessages facesMessages;
      
           /**
            * @return the accountsList
            */
           public List<Account> getAccountsList() {
                return accountsList;
           }
      
           /**
            * @param accountsList the accountsList to set
            */
           public void setAccountsList(List<Account> accountsList) {
                this.accountsList = accountsList;
           }
      
           @Begin(join = true, flushMode = FlushModeType.MANUAL)
           public void edit() {
                if (selectedAccount == null)
                     return;
                selectedAccount = accountService.merge(selectedAccount);
           }
      
           @End
           public void saveEdit() {
                accountService.flush();
                facesMessages.add(FacesMessage.SEVERITY_INFO,
                          "Account #{accountListing.selectedAccount.name} updated.");
                accountsList();
           }
      
           @End
           public void cancelEdit() {
                if (selectedAccount != null) {
                     accountService.merge(selectedAccount);
                     accountService.refresh(selectedAccount);
                     accountService.flush();
                }
                accountsList();
           }
      
           /**
            * @return the selectedAccount
            */
           public Account getSelectedAccount() {
                return selectedAccount;
           }
      
           /**
            * @param selectedAccount the selectedAccount to set
            */
           public void setSelectedAccount(Account selectedAccount) {
                this.selectedAccount = selectedAccount;
           }
      
           /**
            * @return the accountId
            */
           public Long getAccountId() {
                return accountId;
           }
      
           /**
            * @param accountId the accountId to set
            */
           public void setAccountId(Long accountId) {
                this.accountId = accountId;
      
                if (accountId != null) {
                     selectedAccount = accountService.load(accountId);
                } else {
                     selectedAccount = (Account) WebUtil.getValue(Constants.ACCOUNT_KEY);
                }
           }
      }
      
      



      /accounts/edit.xhtml


      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
           xmlns:s="http://jboss.com/products/seam/taglib"
           xmlns:ui="http://java.sun.com/jsf/facelets"
           xmlns:f="http://java.sun.com/jsf/core"
           xmlns:h="http://java.sun.com/jsf/html"
           xmlns:rich="http://richfaces.org/rich"
           xmlns:a4j="http://richfaces.org/a4j"
           template="../layout/template.xhtml">
      
           <ui:define name="body">
                <h:form id="editForm">
                     <h:messages globalOnly="true" />
      
                     <rich:panel>
                          <f:facet name="header">Contact Information</f:facet>
      
                          <fieldset>
                               <s:decorate id="nameDecoration"     template="../layout/edit.xhtml">
                                    <ui:define name="label">Name:</ui:define>
                                    <h:inputText value="#{accountListing.selectedAccount.name}" required="true">
                                         <a4j:support event="onblur" reRender="nameDecoration" />
                                    </h:inputText>
                               </s:decorate>
                               <s:decorate id="emailDecoration" template="../layout/edit.xhtml">
                                    <ui:define name="label">Email:</ui:define>
                                    <h:inputText value="#{accountListing.selectedAccount.email}" required="true">
                                         <a4j:support event="onblur" reRender="emailDecoration" />
                                    </h:inputText>
                               </s:decorate>
                          </fieldset>
                     </rich:panel>
                     <div class="actionButtons">
                          <h:commandButton value="Save" action="#{accountListing.saveEdit}" />
                          <h:commandButton value="Cancel" action="#{accountListing.cancelEdit}" />
                     </div>
                </h:form>
           </ui:define>
      </ui:composition>
      



      /accounts/edit.page.xml


      <?xml version="1.0" encoding="UTF-8"?>
      <page xmlns="http://jboss.com/products/seam/pages"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd">
         Edit Account: #{accountListing.selectedAccount.name}
         <restrict>#{s:hasRole('staff')}</restrict>
         
         <param name="accountId" value="#{accountListing.accountId}" converterId="javax.faces.Long" />
         
         <navigation from-action="#{accountListing.saveEdit}">
             <redirect view-id="/accounts/list.xhtml"/>
         </navigation>
         
         <navigation from-action="#{accountListing.cancelEdit}">
             <redirect view-id="/accounts/list.xhtml"/>
         </navigation>
      </page>
      



      /accounts/list.xhtml


      <!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <ui:composition xmlns="http://www.w3.org/1999/xhtml"
                      xmlns:s="http://jboss.com/products/seam/taglib"
                      xmlns:ui="http://java.sun.com/jsf/facelets"
                      xmlns:f="http://java.sun.com/jsf/core"
                      xmlns:h="http://java.sun.com/jsf/html"
                      xmlns:rich="http://richfaces.org/rich"
                      xmlns:a4j="http://richfaces.org/a4j"
                      template="../layout/template.xhtml">
                      
      <ui:define name="body">
           <h:messages globalOnly="true" styleClass="message" />
          
          <h:form>
              <rich:dataTable id="accountsList" rows="10" value="#{accountsList}" var="account">
                  <f:facet name="header">
                       <rich:columnGroup>
                          <rich:column colspan="4">
                              <h:outputText value="MyApp Accounts" />
                          </rich:column>
                          <rich:column breakBefore="true">
                              <h:outputText value="Actions" />
                          </rich:column>
                          <rich:column>
                              <h:outputText value="Name" />
                          </rich:column>
                          <rich:column>
                              <h:outputText value="Email" />
                          </rich:column>
                          <rich:column>
                              <h:outputText value="Created" />
                          </rich:column>
                      </rich:columnGroup>
                  </f:facet>
                  <rich:column>
                       <rich:dropDownMenu>
                           <f:facet name="label"> 
                               <h:graphicImage value="/img/buttons/showActions.gif" styleClass="pic"/>
                           </f:facet>
                           <rich:menuItem submitMode="server" value="Edit"
                               action="#{accountListing.edit}" icon="/img/buttons/edit.png" />
                              ...
                       </rich:dropDownMenu>
                  </rich:column>
                  <rich:column sortBy="#{account.name}">
                      <h:outputText value="#{account.name}" />
                  </rich:column> 
                  <rich:column sortBy="#{account.email}">
                       <h:outputText value="#{account.email}"/> 
                  </rich:column>
                  <rich:column sortBy="#{account.created}">
                       <h:outputText value="#{account.created}">
                            <f:convertDateTime type="both" />
                       </h:outputText>
                  </rich:column>
              </rich:dataTable>
          </h:form>
        </ui:define>
      </ui:composition>
      



      components.xml


      <?xml version="1.0" encoding="UTF-8"?>
      <components xmlns="http://jboss.com/products/seam/components"
                  xmlns:core="http://jboss.com/products/seam/core"
                  xmlns:persistence="http://jboss.com/products/seam/persistence"
                  xmlns:drools="http://jboss.com/products/seam/drools"
                  xmlns:bpm="http://jboss.com/products/seam/bpm"
                  xmlns:security="http://jboss.com/products/seam/security"
                  xmlns:mail="http://jboss.com/products/seam/mail"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:spring="http://jboss.com/products/seam/spring"
                  xsi:schemaLocation=
                      "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd 
                       http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.0.xsd 
                       http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.0.xsd
                       http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.0.xsd
                       http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.0.xsd
                       http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.0.xsd
                       http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd
                       http://jboss.com/products/seam/spring
                       http://jboss.com/products/seam/spring-2.0.xsd">
      
         <core:init debug="@debug@" jndi-pattern="@jndiPattern@"/>
           
         <core:manager concurrent-request-timeout="500" 
                       conversation-timeout="120000" 
                       conversation-id-parameter="cid"/>
          
         <persistence:managed-persistence-context name="entityManager"
                                           auto-create="true"
                                entity-manager-factory="#{entityManagerFactory}"/>
      
         <drools:rule-base name="securityRules">
             <drools:rule-files><value>/security.drl</value></drools:rule-files>
         </drools:rule-base>
      
         <security:identity authenticate-method="#{authenticator.authenticate}"
                                 security-rules="#{securityRules}"
                                    remember-me="true"/>
                                    
         <event type="org.jboss.seam.security.notLoggedIn">
             <action execute="#{redirect.captureCurrentView}"/>
         </event>
         <event type="org.jboss.seam.security.loginSuccessful">
             <action execute="#{redirect.returnToCapturedView}"/>
         </event>
         
         <spring:context-loader>
           <spring:config-locations>
             <value>classpath:coreResourceContext.xml</value>
             <value>classpath:daoContext.xml</value>
             <value>classpath:modelContext.xml</value>
             <value>classpath:applicationContext.xml</value>
           </spring:config-locations>
         </spring:context-loader>
         
         <spring:spring-transaction platform-transaction-manager="#{transactionManager}"/>
            
      </components>
      



      applicationContext.xml


      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                                         http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                                         http://www.springframework.org/schema/tx
                                         http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
                                         
           <!-- Persistence Definitions -->
           <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
                <property name="jndiName" value="java:/MyAppWebDatasource" />
           </bean>
                                         
           <bean id="entityManagerFactory"
                class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
                <property name="persistenceUnitName" value="myapp" />
                <property name="dataSource" ref="dataSource" />
                <property name="jpaVendorAdapter">
                     <bean
                          class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                          <property name="database" value="MYSQL" />
                          <property name="databasePlatform"
                               value="org.hibernate.dialect.MySQLDialect" />
                          <property name="showSql" value="true" />
                          <property name="generateDdl" value="false" />
                     </bean>
                </property>
           </bean>
                                         
           <bean id="transactionManager"
                class="org.springframework.transaction.jta.JtaTransactionManager" />
           
           <bean id="seamEntityManagerFactory" class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">
                   <property name="persistenceContextName" value="entityManager"/>
                <property name="persistenceUnitName" value="myapp"/>
           </bean>
           
           <bean id="accountDao"
                class="myapp.framework.account.dao.AccountJpaDaoImpl">
                <property name="entityManagerFactory"
                     ref="seamEntityManagerFactory" />
           </bean>
      ...
      </beans>
      



      The remaining files mainly include models, the applicationContext.xml is the meat of the Spring configuration for the app.

        • 1. Re: Refreshing Model objects after cancel
          cmoerz.seam.ny-central.org

          No idea if this is still an open issue, I'll just post what I've done to get this kind of thing working.


          I read the thread at http://www.seamframework.org/Community/SbuttonInsideViewComponentDoesNotWork and realized, that if you change your xhtml to something like the following, it should work:


          ...
          <h:commandButton value="Save Changes" action="#{beanEdit.save}" /> 
          <s:button value="Cancel" action="#{beanEdit.cancel}" propagation="end" view="view.xhtml" />
          ...
          



          That means, that when you press Save Changes, the save function gets called, else you are directly brought to your view page and the cancel method gets called. I'd also recommend putting everything into a conversation and annotation cancel with a @End accordingly, but that's just my current approach. I'm new to Seam, so take the whole thing with a grain of salt, I guess :)


          good luck
          chris

          • 2. Re: Refreshing Model objects after cancel
            janscherf

            We run into exactly the above described situation.

            Annotating the cancelEdit()-method with @End(beforeRedirect=true) solved it.


            Thanks to my colleague Dirk ;-)