You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Greg Pagendam-Turner <gr...@liftyourgame.com> on 2010/03/14 04:11:17 UTC

AjaxFormLoop - Please help

Guys,

I'm trying to work out how to fix the AjaxFormLoop example in Jump Start.

http://jumpstart.doublenegative.com.au:8080/jumpstart/examples/tables/ajaxformloop1

The code recommends that it should use conversations instead of session
persistence. I've changed the code to use conversations but still get
the hidden from field element error when clicking the "Add Row" link as
mentioned in the JIRA https://issues.apache.org/jira/browse/TAP5-733.

This is because there is nothing in the rendered partial xml that
contains a tag that the hidden field can be placed after (such as input,
select, textarea, label, p, div, td or li).

Stepping through the code shows that the markup retruned when clicking
Add Row is just:
<ajax-partial></ajax-partial>

I'd appreciate any clues on where to look next please.

Regards,

Greg.

PS: Modified AjaxFormLoop1.java is

package jumpstart.web.pages.examples.tables;

import java.text.DateFormat;
import java.text.Format;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import jumpstart.business.domain.examples.Person;
import jumpstart.business.domain.examples.iface.IPersonServiceLocal;
import jumpstart.client.IBusinessServicesLocator;
import jumpstart.web.commons.Conversation;
import jumpstart.web.commons.Conversations;
import jumpstart.web.commons.ExceptionUtil;
import jumpstart.web.pages.Index;
import jumpstart.web.pages.examples.wizard.WizardUsingComponents.Step;
import jumpstart.web.state.examples.wizard.CreditRequest;

import org.apache.tapestry5.BindingConstants;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.EventContext;
import org.apache.tapestry5.ValueEncoder;
import org.apache.tapestry5.annotations.Component;
import org.apache.tapestry5.annotations.InjectPage;
import org.apache.tapestry5.annotations.Parameter;
import org.apache.tapestry5.annotations.Persist;
import org.apache.tapestry5.annotations.Property;
import org.apache.tapestry5.annotations.SessionState;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.ioc.annotations.Inject;

public class AjaxFormLoop1 {
      private String _conversationId = null;

      @SessionState
      private Conversations _conversations;

      // We've used "session" persistence but it is risky. Better
techniques include wrapping this field in a
      // Conversation (see the Wizard examples) or persisting it in the
database.
      @Parameter(defaultPrefix = BindingConstants.PROP)
      @Property
      //@Persist
      private List<PersonHolder>  _personHolders;

      @SuppressWarnings("unused")
      @Property
      private PersonHolder _personHolder;

      private List<Person>  _persons;

      @Component(id = "personsedit")
      private Form _form;

      @Inject
      private IBusinessServicesLocator _businessServicesLocator;

      @InjectPage
      private AjaxFormLoop2 _page2;

      @Inject
      private Locale _currentLocale;

      @Inject
      private ComponentResources _resources;

      Object[] onPassivate() {
          return new Object[] { _conversationId };
      }

      Object onActivate(EventContext context) throws Exception {
          if (context.getCount() == 0) {
              setupPersonHolders();
              _conversationId = startConversation(_personHolders);
          }
          else
          {
              _conversationId = context.get(String.class, 0);
          }

          _personHolders = getPersonHoldersFromConversation(_conversationId);

          return null;
      }

      void setupPersonHolders()
      {
          // Get all persons - ask business service to find them (from
the database)
          _persons = getPersonService().findPersons();

          _personHolders = new ArrayList<PersonHolder>();
          for (Person person : _persons) {
              _personHolders.add(new PersonHolder(person, false,
person.getId()));
          }
      }

      // Form triggers the PREPARE event during form render and form
submission.

      void onPrepare() {
          if (!_form.getHasErrors()) {
              if (_personHolders == null) {
                  //setupPersonHolders();
                  _personHolders =
getPersonHoldersFromConversation(_conversationId);
              }
          }
      }

      PersonHolder onAddRow() {
          // Create a skeleton Person and add it to the displayed list
with a unique key
          Person newPerson = new Person();
          PersonHolder newPersonHolder = new PersonHolder(newPerson,
true, 0 - System.nanoTime());
          _personHolders.add(newPersonHolder);

          return newPersonHolder;
      }

      void onRemoveRow(PersonHolder personPlus) {
          int index = _personHolders.indexOf(personPlus);
          PersonHolder holder = _personHolders.get(index);

          // If the person is new, remove them from the list. Else, flag
them to be deleted from the database.
          if (holder.isNew()) {
              _personHolders.remove(personPlus);
          }
          else {
              holder.setDeleted(true);
          }
      }

      void onValidateForm() {
          List<Person>  personsToCreate = new ArrayList<Person>();
          List<Person>  personsToChange = new ArrayList<Person>();
          List<Person>  personsToDelete = new ArrayList<Person>();

          for (PersonHolder holder : _personHolders) {
              if (holder.isNew()) {
                  personsToCreate.add(holder.getPerson());
              }
              else if (holder.isDeleted()) {
                  personsToDelete.add(holder.getPerson());
              }
              else {
                  personsToChange.add(holder.getPerson());
              }
          }

          System.out.println(">>>  personsToCreate = " + personsToCreate);
          System.out.println(">>>  personsToChange = " + personsToChange);
          System.out.println(">>>  personsToDelete = " + personsToDelete);

          try {
              // In a real application you would persist them to the
database instead of printing them
              // getPersonService().bulkEditPersons(personsToCreate,
personsToChange, personsToDelete);
          }
          catch (Exception e) {
              // Display the cause. In a real system we would try harder
to get a user-friendly message.
              _form.recordError(ExceptionUtil.getRootCause(e));
          }
      }

      Object onSuccess() {
          List<Person>  persons = new ArrayList<Person>();
          for (PersonHolder holder : _personHolders) {
              if (!holder.isDeleted()) {
                  persons.add(holder.getPerson());
              }
          }
          _page2.set(persons);
          endConversation(_conversationId);
          _resources.discardPersistentFieldChanges();
          return _page2;
      }

      void onRefresh() {
          _resources.discardPersistentFieldChanges();
          onPrepare();
      }

      Object onActionFromGoHome() {
          _resources.discardPersistentFieldChanges();
          return Index.class;
      }

      @SuppressWarnings("unchecked")
      public ValueEncoder getEncoder() {
          return new ValueEncoder<PersonHolder>() {

              public String toClient(PersonHolder value) {
                  Long key = value.getKey();
                  return key.toString();
              }

              public PersonHolder toValue(String keyAsString) {
                  Long key = new Long(keyAsString);
                  for (PersonHolder holder : _personHolders) {
                      if (holder.getKey().equals(key)) {
                          return holder;
                      }
                  }
                  throw new IllegalArgumentException("Received key \"" + key
                          + "\" which has no counterpart in this
collection: " + _personHolders);
              }
          };
      }

      public class PersonHolder {
          private Person _person;
          private Long _key;
          private boolean _new;
          private boolean _deleted;

          PersonHolder(Person person, boolean newPerson, Long key) {
              _person = person;
              _new = newPerson;
              _key = key;
          }

          public Person getPerson() {
              return _person;
          }

          public Long getKey() {
              return _key;
          }

          public boolean isNew() {
              return _new;
          }

          public boolean setDeleted(boolean deleted) {
              return _deleted = deleted;
          }

          public boolean isDeleted() {
              return _deleted;
          }
      }

      private IPersonServiceLocal getPersonService() {
          // Use our business services locator to get the EJB3 session
bean called "PersonServiceLocal".
          return _businessServicesLocator.getPersonServiceLocal();
      }

      public Format getDateFormat() {
          return DateFormat.getDateInstance(DateFormat.SHORT,
_currentLocale);
      }

      private String startConversation(Object target) {
          String conversationId = Long.toString(System.currentTimeMillis());
          _conversations.add(new Conversation(conversationId, target));
          return conversationId;
      }

      private void endConversation(String conversationId) {
          _conversations.remove(conversationId);

          // If conversations ASO is now empty then remove it from the
session

          if (_conversations.isEmpty()) {
              _conversations = null;
          }
      }

      private List<PersonHolder>  getPersonHoldersFromConversation(String
conversationId) {
          Conversation conversation = _conversations.get(conversationId);
          if (conversation != null&&  conversation.getTarget() instanceof
List<?>) {
              return (List<PersonHolder>) conversation.getTarget();
          }
          return null;
      }

}



---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: AjaxFormLoop - Please help

Posted by Greg Pagendam-Turner <gr...@liftyourgame.com>.
Howard, Thiago,

Would adding <ajax-partial> to the list of Hidden Field Location Rules 
fix TAP5-733?

Regards,

Greg.


On 15/03/2010 6:25 AM, Greg Pagendam-Turner wrote:
> Geoff,
>
> I tried your onActivate method. The conversation id is now in the url 
> but I'm still getting thehidden form field error.
>
> Regards,
>
> Greg
>
> On 14/03/2010 9:39 PM, Geoff Callender wrote:
>> I don't think this is the TAP5-733 bug. Running the code I've 
>> recreated the exception and yes, Blackbird reported it as a 
>> communication error, but the log shows Its root cause was an NPE 
>> caused by _personHolders being null.
>>
>> Caused by: java.lang.NullPointerException
>>     at 
>> jumpstart.web.pages.examples.tables.AjaxFormLoop1New.onAddRow(AjaxFormLoop1New.java:106) 
>>
>>     at 
>> jumpstart.web.pages.examples.tables.AjaxFormLoop1New.dispatchComponentEvent(AjaxFormLoop1New.java) 
>>
>>     at 
>> org.apache.tapestry5.internal.structure.ComponentPageElementImpl.dispatchEvent(ComponentPageElementImpl.java:902) 
>>
>>     at 
>> org.apache.tapestry5.internal.structure.ComponentPageElementImpl.triggerContextEvent(ComponentPageElementImpl.java:1081) 
>>
>>     ... 68 more
>>
>> I think it's due to the conversation id never appearing in the page 
>> context. Try this change to onActivate() - when you start a 
>> conversation redirect to the same page so that onPassivate() gets 
>> called and therefore the conversation id goes into the URL.
>>
>>     Object onActivate(EventContext context) throws Exception {
>>         if (context.getCount() == 0) {
>>             setupPersonHolders();
>>             _conversationId = startConversation(_personHolders);
>>             return this;
>>         }
>>         else {
>>             _conversationId = context.get(String.class, 0);
>>             _personHolders = 
>> getPersonHoldersFromConversation(_conversationId);
>>             if (_personHolders == null) {
>>                 setupPersonHolders();
>>                 _conversationId = startConversation(_personHolders);
>>                 return this;
>>             }
>>         }
>>
>>         return null;
>>     }
>>
>> Also, don't get in the habit of initialising fields in the 
>> declaration, eg.
>>
>>>      private String _conversationId = null;
>>
>> The reason is that you'll rarely get a freshly instantiated page - 
>> more likely is you'll get one that Tapestry has pulled out of its 
>> pool of page instances.
>>
>> Cheers,
>>
>> Geoff
>>
>>
>> On 14/03/2010, at 2:11 PM, Greg Pagendam-Turner wrote:
>>
>>> Guys,
>>>
>>> I'm trying to work out how to fix the AjaxFormLoop example in Jump 
>>> Start.
>>>
>>> http://jumpstart.doublenegative.com.au:8080/jumpstart/examples/tables/ajaxformloop1 
>>>
>>>
>>> The code recommends that it should use conversations instead of session
>>> persistence. I've changed the code to use conversations but still get
>>> the hidden from field element error when clicking the "Add Row" link as
>>> mentioned in the JIRA https://issues.apache.org/jira/browse/TAP5-733.
>>>
>>> This is because there is nothing in the rendered partial xml that
>>> contains a tag that the hidden field can be placed after (such as 
>>> input,
>>> select, textarea, label, p, div, td or li).
>>>
>>> Stepping through the code shows that the markup retruned when clicking
>>> Add Row is just:
>>> <ajax-partial></ajax-partial>
>>>
>>> I'd appreciate any clues on where to look next please.
>>>
>>> Regards,
>>>
>>> Greg.
>>>
>>> PS: Modified AjaxFormLoop1.java is
>>>
>>> package jumpstart.web.pages.examples.tables;
>>>
>>> import java.text.DateFormat;
>>> import java.text.Format;
>>> import java.util.ArrayList;
>>> import java.util.List;
>>> import java.util.Locale;
>>>
>>> import jumpstart.business.domain.examples.Person;
>>> import jumpstart.business.domain.examples.iface.IPersonServiceLocal;
>>> import jumpstart.client.IBusinessServicesLocator;
>>> import jumpstart.web.commons.Conversation;
>>> import jumpstart.web.commons.Conversations;
>>> import jumpstart.web.commons.ExceptionUtil;
>>> import jumpstart.web.pages.Index;
>>> import jumpstart.web.pages.examples.wizard.WizardUsingComponents.Step;
>>> import jumpstart.web.state.examples.wizard.CreditRequest;
>>>
>>> import org.apache.tapestry5.BindingConstants;
>>> import org.apache.tapestry5.ComponentResources;
>>> import org.apache.tapestry5.EventContext;
>>> import org.apache.tapestry5.ValueEncoder;
>>> import org.apache.tapestry5.annotations.Component;
>>> import org.apache.tapestry5.annotations.InjectPage;
>>> import org.apache.tapestry5.annotations.Parameter;
>>> import org.apache.tapestry5.annotations.Persist;
>>> import org.apache.tapestry5.annotations.Property;
>>> import org.apache.tapestry5.annotations.SessionState;
>>> import org.apache.tapestry5.corelib.components.Form;
>>> import org.apache.tapestry5.ioc.annotations.Inject;
>>>
>>> public class AjaxFormLoop1 {
>>>      private String _conversationId = null;
>>>
>>>      @SessionState
>>>      private Conversations _conversations;
>>>
>>>      // We've used "session" persistence but it is risky. Better
>>> techniques include wrapping this field in a
>>>      // Conversation (see the Wizard examples) or persisting it in the
>>> database.
>>>      @Parameter(defaultPrefix = BindingConstants.PROP)
>>>      @Property
>>>      //@Persist
>>>      private List<PersonHolder>   _personHolders;
>>>
>>>      @SuppressWarnings("unused")
>>>      @Property
>>>      private PersonHolder _personHolder;
>>>
>>>      private List<Person>   _persons;
>>>
>>>      @Component(id = "personsedit")
>>>      private Form _form;
>>>
>>>      @Inject
>>>      private IBusinessServicesLocator _businessServicesLocator;
>>>
>>>      @InjectPage
>>>      private AjaxFormLoop2 _page2;
>>>
>>>      @Inject
>>>      private Locale _currentLocale;
>>>
>>>      @Inject
>>>      private ComponentResources _resources;
>>>
>>>      Object[] onPassivate() {
>>>          return new Object[] { _conversationId };
>>>      }
>>>
>>>      Object onActivate(EventContext context) throws Exception {
>>>          if (context.getCount() == 0) {
>>>              setupPersonHolders();
>>>              _conversationId = startConversation(_personHolders);
>>>          }
>>>          else
>>>          {
>>>              _conversationId = context.get(String.class, 0);
>>>          }
>>>
>>>          _personHolders = 
>>> getPersonHoldersFromConversation(_conversationId);
>>>
>>>          return null;
>>>      }
>>>
>>>      void setupPersonHolders()
>>>      {
>>>          // Get all persons - ask business service to find them (from
>>> the database)
>>>          _persons = getPersonService().findPersons();
>>>
>>>          _personHolders = new ArrayList<PersonHolder>();
>>>          for (Person person : _persons) {
>>>              _personHolders.add(new PersonHolder(person, false,
>>> person.getId()));
>>>          }
>>>      }
>>>
>>>      // Form triggers the PREPARE event during form render and form
>>> submission.
>>>
>>>      void onPrepare() {
>>>          if (!_form.getHasErrors()) {
>>>              if (_personHolders == null) {
>>>                  //setupPersonHolders();
>>>                  _personHolders =
>>> getPersonHoldersFromConversation(_conversationId);
>>>              }
>>>          }
>>>      }
>>>
>>>      PersonHolder onAddRow() {
>>>          // Create a skeleton Person and add it to the displayed list
>>> with a unique key
>>>          Person newPerson = new Person();
>>>          PersonHolder newPersonHolder = new PersonHolder(newPerson,
>>> true, 0 - System.nanoTime());
>>>          _personHolders.add(newPersonHolder);
>>>
>>>          return newPersonHolder;
>>>      }
>>>
>>>      void onRemoveRow(PersonHolder personPlus) {
>>>          int index = _personHolders.indexOf(personPlus);
>>>          PersonHolder holder = _personHolders.get(index);
>>>
>>>          // If the person is new, remove them from the list. Else, flag
>>> them to be deleted from the database.
>>>          if (holder.isNew()) {
>>>              _personHolders.remove(personPlus);
>>>          }
>>>          else {
>>>              holder.setDeleted(true);
>>>          }
>>>      }
>>>
>>>      void onValidateForm() {
>>>          List<Person>   personsToCreate = new ArrayList<Person>();
>>>          List<Person>   personsToChange = new ArrayList<Person>();
>>>          List<Person>   personsToDelete = new ArrayList<Person>();
>>>
>>>          for (PersonHolder holder : _personHolders) {
>>>              if (holder.isNew()) {
>>>                  personsToCreate.add(holder.getPerson());
>>>              }
>>>              else if (holder.isDeleted()) {
>>>                  personsToDelete.add(holder.getPerson());
>>>              }
>>>              else {
>>>                  personsToChange.add(holder.getPerson());
>>>              }
>>>          }
>>>
>>>          System.out.println(">>>   personsToCreate = " + 
>>> personsToCreate);
>>>          System.out.println(">>>   personsToChange = " + 
>>> personsToChange);
>>>          System.out.println(">>>   personsToDelete = " + 
>>> personsToDelete);
>>>
>>>          try {
>>>              // In a real application you would persist them to the
>>> database instead of printing them
>>>              // getPersonService().bulkEditPersons(personsToCreate,
>>> personsToChange, personsToDelete);
>>>          }
>>>          catch (Exception e) {
>>>              // Display the cause. In a real system we would try harder
>>> to get a user-friendly message.
>>>              _form.recordError(ExceptionUtil.getRootCause(e));
>>>          }
>>>      }
>>>
>>>      Object onSuccess() {
>>>          List<Person>   persons = new ArrayList<Person>();
>>>          for (PersonHolder holder : _personHolders) {
>>>              if (!holder.isDeleted()) {
>>>                  persons.add(holder.getPerson());
>>>              }
>>>          }
>>>          _page2.set(persons);
>>>          endConversation(_conversationId);
>>>          _resources.discardPersistentFieldChanges();
>>>          return _page2;
>>>      }
>>>
>>>      void onRefresh() {
>>>          _resources.discardPersistentFieldChanges();
>>>          onPrepare();
>>>      }
>>>
>>>      Object onActionFromGoHome() {
>>>          _resources.discardPersistentFieldChanges();
>>>          return Index.class;
>>>      }
>>>
>>>      @SuppressWarnings("unchecked")
>>>      public ValueEncoder getEncoder() {
>>>          return new ValueEncoder<PersonHolder>() {
>>>
>>>              public String toClient(PersonHolder value) {
>>>                  Long key = value.getKey();
>>>                  return key.toString();
>>>              }
>>>
>>>              public PersonHolder toValue(String keyAsString) {
>>>                  Long key = new Long(keyAsString);
>>>                  for (PersonHolder holder : _personHolders) {
>>>                      if (holder.getKey().equals(key)) {
>>>                          return holder;
>>>                      }
>>>                  }
>>>                  throw new IllegalArgumentException("Received key 
>>> \"" + key
>>>                          + "\" which has no counterpart in this
>>> collection: " + _personHolders);
>>>              }
>>>          };
>>>      }
>>>
>>>      public class PersonHolder {
>>>          private Person _person;
>>>          private Long _key;
>>>          private boolean _new;
>>>          private boolean _deleted;
>>>
>>>          PersonHolder(Person person, boolean newPerson, Long key) {
>>>              _person = person;
>>>              _new = newPerson;
>>>              _key = key;
>>>          }
>>>
>>>          public Person getPerson() {
>>>              return _person;
>>>          }
>>>
>>>          public Long getKey() {
>>>              return _key;
>>>          }
>>>
>>>          public boolean isNew() {
>>>              return _new;
>>>          }
>>>
>>>          public boolean setDeleted(boolean deleted) {
>>>              return _deleted = deleted;
>>>          }
>>>
>>>          public boolean isDeleted() {
>>>              return _deleted;
>>>          }
>>>      }
>>>
>>>      private IPersonServiceLocal getPersonService() {
>>>          // Use our business services locator to get the EJB3 session
>>> bean called "PersonServiceLocal".
>>>          return _businessServicesLocator.getPersonServiceLocal();
>>>      }
>>>
>>>      public Format getDateFormat() {
>>>          return DateFormat.getDateInstance(DateFormat.SHORT,
>>> _currentLocale);
>>>      }
>>>
>>>      private String startConversation(Object target) {
>>>          String conversationId = 
>>> Long.toString(System.currentTimeMillis());
>>>          _conversations.add(new Conversation(conversationId, target));
>>>          return conversationId;
>>>      }
>>>
>>>      private void endConversation(String conversationId) {
>>>          _conversations.remove(conversationId);
>>>
>>>          // If conversations ASO is now empty then remove it from the
>>> session
>>>
>>>          if (_conversations.isEmpty()) {
>>>              _conversations = null;
>>>          }
>>>      }
>>>
>>>      private List<PersonHolder>   
>>> getPersonHoldersFromConversation(String
>>> conversationId) {
>>>          Conversation conversation = 
>>> _conversations.get(conversationId);
>>>          if (conversation != null&&   conversation.getTarget() 
>>> instanceof
>>> List<?>) {
>>>              return (List<PersonHolder>) conversation.getTarget();
>>>          }
>>>          return null;
>>>      }
>>>
>>> }
>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>>> For additional commands, e-mail: users-help@tapestry.apache.org
>>>
>>
>


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: AjaxFormLoop - Please help

Posted by Greg Pagendam-Turner <gr...@liftyourgame.com>.
Geoff,

I tried your onActivate method. The conversation id is now in the url 
but I'm still getting thehidden form field error.

Regards,

Greg

On 14/03/2010 9:39 PM, Geoff Callender wrote:
> I don't think this is the TAP5-733 bug. Running the code I've recreated the exception and yes, Blackbird reported it as a communication error, but the log shows Its root cause was an NPE caused by _personHolders being null.
>
> Caused by: java.lang.NullPointerException
> 	at jumpstart.web.pages.examples.tables.AjaxFormLoop1New.onAddRow(AjaxFormLoop1New.java:106)
> 	at jumpstart.web.pages.examples.tables.AjaxFormLoop1New.dispatchComponentEvent(AjaxFormLoop1New.java)
> 	at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.dispatchEvent(ComponentPageElementImpl.java:902)
> 	at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.triggerContextEvent(ComponentPageElementImpl.java:1081)
> 	... 68 more
>
> I think it's due to the conversation id never appearing in the page context. Try this change to onActivate() - when you start a conversation redirect to the same page so that onPassivate() gets called and therefore the conversation id goes into the URL.
>
> 	Object onActivate(EventContext context) throws Exception {
> 		if (context.getCount() == 0) {
> 			setupPersonHolders();
> 			_conversationId = startConversation(_personHolders);
> 			return this;
> 		}
> 		else {
> 			_conversationId = context.get(String.class, 0);
> 			_personHolders = getPersonHoldersFromConversation(_conversationId);
> 			if (_personHolders == null) {
> 				setupPersonHolders();
> 				_conversationId = startConversation(_personHolders);
> 				return this;
> 			}
> 		}
>
> 		return null;
> 	}
>
> Also, don't get in the habit of initialising fields in the declaration, eg.
>
>    
>>      private String _conversationId = null;
>>      
>
> The reason is that you'll rarely get a freshly instantiated page - more likely is you'll get one that Tapestry has pulled out of its pool of page instances.
>
> Cheers,
>
> Geoff
>
>
> On 14/03/2010, at 2:11 PM, Greg Pagendam-Turner wrote:
>
>    
>> Guys,
>>
>> I'm trying to work out how to fix the AjaxFormLoop example in Jump Start.
>>
>> http://jumpstart.doublenegative.com.au:8080/jumpstart/examples/tables/ajaxformloop1
>>
>> The code recommends that it should use conversations instead of session
>> persistence. I've changed the code to use conversations but still get
>> the hidden from field element error when clicking the "Add Row" link as
>> mentioned in the JIRA https://issues.apache.org/jira/browse/TAP5-733.
>>
>> This is because there is nothing in the rendered partial xml that
>> contains a tag that the hidden field can be placed after (such as input,
>> select, textarea, label, p, div, td or li).
>>
>> Stepping through the code shows that the markup retruned when clicking
>> Add Row is just:
>> <ajax-partial></ajax-partial>
>>
>> I'd appreciate any clues on where to look next please.
>>
>> Regards,
>>
>> Greg.
>>
>> PS: Modified AjaxFormLoop1.java is
>>
>> package jumpstart.web.pages.examples.tables;
>>
>> import java.text.DateFormat;
>> import java.text.Format;
>> import java.util.ArrayList;
>> import java.util.List;
>> import java.util.Locale;
>>
>> import jumpstart.business.domain.examples.Person;
>> import jumpstart.business.domain.examples.iface.IPersonServiceLocal;
>> import jumpstart.client.IBusinessServicesLocator;
>> import jumpstart.web.commons.Conversation;
>> import jumpstart.web.commons.Conversations;
>> import jumpstart.web.commons.ExceptionUtil;
>> import jumpstart.web.pages.Index;
>> import jumpstart.web.pages.examples.wizard.WizardUsingComponents.Step;
>> import jumpstart.web.state.examples.wizard.CreditRequest;
>>
>> import org.apache.tapestry5.BindingConstants;
>> import org.apache.tapestry5.ComponentResources;
>> import org.apache.tapestry5.EventContext;
>> import org.apache.tapestry5.ValueEncoder;
>> import org.apache.tapestry5.annotations.Component;
>> import org.apache.tapestry5.annotations.InjectPage;
>> import org.apache.tapestry5.annotations.Parameter;
>> import org.apache.tapestry5.annotations.Persist;
>> import org.apache.tapestry5.annotations.Property;
>> import org.apache.tapestry5.annotations.SessionState;
>> import org.apache.tapestry5.corelib.components.Form;
>> import org.apache.tapestry5.ioc.annotations.Inject;
>>
>> public class AjaxFormLoop1 {
>>      private String _conversationId = null;
>>
>>      @SessionState
>>      private Conversations _conversations;
>>
>>      // We've used "session" persistence but it is risky. Better
>> techniques include wrapping this field in a
>>      // Conversation (see the Wizard examples) or persisting it in the
>> database.
>>      @Parameter(defaultPrefix = BindingConstants.PROP)
>>      @Property
>>      //@Persist
>>      private List<PersonHolder>   _personHolders;
>>
>>      @SuppressWarnings("unused")
>>      @Property
>>      private PersonHolder _personHolder;
>>
>>      private List<Person>   _persons;
>>
>>      @Component(id = "personsedit")
>>      private Form _form;
>>
>>      @Inject
>>      private IBusinessServicesLocator _businessServicesLocator;
>>
>>      @InjectPage
>>      private AjaxFormLoop2 _page2;
>>
>>      @Inject
>>      private Locale _currentLocale;
>>
>>      @Inject
>>      private ComponentResources _resources;
>>
>>      Object[] onPassivate() {
>>          return new Object[] { _conversationId };
>>      }
>>
>>      Object onActivate(EventContext context) throws Exception {
>>          if (context.getCount() == 0) {
>>              setupPersonHolders();
>>              _conversationId = startConversation(_personHolders);
>>          }
>>          else
>>          {
>>              _conversationId = context.get(String.class, 0);
>>          }
>>
>>          _personHolders = getPersonHoldersFromConversation(_conversationId);
>>
>>          return null;
>>      }
>>
>>      void setupPersonHolders()
>>      {
>>          // Get all persons - ask business service to find them (from
>> the database)
>>          _persons = getPersonService().findPersons();
>>
>>          _personHolders = new ArrayList<PersonHolder>();
>>          for (Person person : _persons) {
>>              _personHolders.add(new PersonHolder(person, false,
>> person.getId()));
>>          }
>>      }
>>
>>      // Form triggers the PREPARE event during form render and form
>> submission.
>>
>>      void onPrepare() {
>>          if (!_form.getHasErrors()) {
>>              if (_personHolders == null) {
>>                  //setupPersonHolders();
>>                  _personHolders =
>> getPersonHoldersFromConversation(_conversationId);
>>              }
>>          }
>>      }
>>
>>      PersonHolder onAddRow() {
>>          // Create a skeleton Person and add it to the displayed list
>> with a unique key
>>          Person newPerson = new Person();
>>          PersonHolder newPersonHolder = new PersonHolder(newPerson,
>> true, 0 - System.nanoTime());
>>          _personHolders.add(newPersonHolder);
>>
>>          return newPersonHolder;
>>      }
>>
>>      void onRemoveRow(PersonHolder personPlus) {
>>          int index = _personHolders.indexOf(personPlus);
>>          PersonHolder holder = _personHolders.get(index);
>>
>>          // If the person is new, remove them from the list. Else, flag
>> them to be deleted from the database.
>>          if (holder.isNew()) {
>>              _personHolders.remove(personPlus);
>>          }
>>          else {
>>              holder.setDeleted(true);
>>          }
>>      }
>>
>>      void onValidateForm() {
>>          List<Person>   personsToCreate = new ArrayList<Person>();
>>          List<Person>   personsToChange = new ArrayList<Person>();
>>          List<Person>   personsToDelete = new ArrayList<Person>();
>>
>>          for (PersonHolder holder : _personHolders) {
>>              if (holder.isNew()) {
>>                  personsToCreate.add(holder.getPerson());
>>              }
>>              else if (holder.isDeleted()) {
>>                  personsToDelete.add(holder.getPerson());
>>              }
>>              else {
>>                  personsToChange.add(holder.getPerson());
>>              }
>>          }
>>
>>          System.out.println(">>>   personsToCreate = " + personsToCreate);
>>          System.out.println(">>>   personsToChange = " + personsToChange);
>>          System.out.println(">>>   personsToDelete = " + personsToDelete);
>>
>>          try {
>>              // In a real application you would persist them to the
>> database instead of printing them
>>              // getPersonService().bulkEditPersons(personsToCreate,
>> personsToChange, personsToDelete);
>>          }
>>          catch (Exception e) {
>>              // Display the cause. In a real system we would try harder
>> to get a user-friendly message.
>>              _form.recordError(ExceptionUtil.getRootCause(e));
>>          }
>>      }
>>
>>      Object onSuccess() {
>>          List<Person>   persons = new ArrayList<Person>();
>>          for (PersonHolder holder : _personHolders) {
>>              if (!holder.isDeleted()) {
>>                  persons.add(holder.getPerson());
>>              }
>>          }
>>          _page2.set(persons);
>>          endConversation(_conversationId);
>>          _resources.discardPersistentFieldChanges();
>>          return _page2;
>>      }
>>
>>      void onRefresh() {
>>          _resources.discardPersistentFieldChanges();
>>          onPrepare();
>>      }
>>
>>      Object onActionFromGoHome() {
>>          _resources.discardPersistentFieldChanges();
>>          return Index.class;
>>      }
>>
>>      @SuppressWarnings("unchecked")
>>      public ValueEncoder getEncoder() {
>>          return new ValueEncoder<PersonHolder>() {
>>
>>              public String toClient(PersonHolder value) {
>>                  Long key = value.getKey();
>>                  return key.toString();
>>              }
>>
>>              public PersonHolder toValue(String keyAsString) {
>>                  Long key = new Long(keyAsString);
>>                  for (PersonHolder holder : _personHolders) {
>>                      if (holder.getKey().equals(key)) {
>>                          return holder;
>>                      }
>>                  }
>>                  throw new IllegalArgumentException("Received key \"" + key
>>                          + "\" which has no counterpart in this
>> collection: " + _personHolders);
>>              }
>>          };
>>      }
>>
>>      public class PersonHolder {
>>          private Person _person;
>>          private Long _key;
>>          private boolean _new;
>>          private boolean _deleted;
>>
>>          PersonHolder(Person person, boolean newPerson, Long key) {
>>              _person = person;
>>              _new = newPerson;
>>              _key = key;
>>          }
>>
>>          public Person getPerson() {
>>              return _person;
>>          }
>>
>>          public Long getKey() {
>>              return _key;
>>          }
>>
>>          public boolean isNew() {
>>              return _new;
>>          }
>>
>>          public boolean setDeleted(boolean deleted) {
>>              return _deleted = deleted;
>>          }
>>
>>          public boolean isDeleted() {
>>              return _deleted;
>>          }
>>      }
>>
>>      private IPersonServiceLocal getPersonService() {
>>          // Use our business services locator to get the EJB3 session
>> bean called "PersonServiceLocal".
>>          return _businessServicesLocator.getPersonServiceLocal();
>>      }
>>
>>      public Format getDateFormat() {
>>          return DateFormat.getDateInstance(DateFormat.SHORT,
>> _currentLocale);
>>      }
>>
>>      private String startConversation(Object target) {
>>          String conversationId = Long.toString(System.currentTimeMillis());
>>          _conversations.add(new Conversation(conversationId, target));
>>          return conversationId;
>>      }
>>
>>      private void endConversation(String conversationId) {
>>          _conversations.remove(conversationId);
>>
>>          // If conversations ASO is now empty then remove it from the
>> session
>>
>>          if (_conversations.isEmpty()) {
>>              _conversations = null;
>>          }
>>      }
>>
>>      private List<PersonHolder>   getPersonHoldersFromConversation(String
>> conversationId) {
>>          Conversation conversation = _conversations.get(conversationId);
>>          if (conversation != null&&   conversation.getTarget() instanceof
>> List<?>) {
>>              return (List<PersonHolder>) conversation.getTarget();
>>          }
>>          return null;
>>      }
>>
>> }
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: users-help@tapestry.apache.org
>>
>>      
>
>    


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: AjaxFormLoop - Please help

Posted by Geoff Callender <ge...@gmail.com>.
I don't think this is the TAP5-733 bug. Running the code I've recreated the exception and yes, Blackbird reported it as a communication error, but the log shows Its root cause was an NPE caused by _personHolders being null.

Caused by: java.lang.NullPointerException
	at jumpstart.web.pages.examples.tables.AjaxFormLoop1New.onAddRow(AjaxFormLoop1New.java:106)
	at jumpstart.web.pages.examples.tables.AjaxFormLoop1New.dispatchComponentEvent(AjaxFormLoop1New.java)
	at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.dispatchEvent(ComponentPageElementImpl.java:902)
	at org.apache.tapestry5.internal.structure.ComponentPageElementImpl.triggerContextEvent(ComponentPageElementImpl.java:1081)
	... 68 more

I think it's due to the conversation id never appearing in the page context. Try this change to onActivate() - when you start a conversation redirect to the same page so that onPassivate() gets called and therefore the conversation id goes into the URL.

	Object onActivate(EventContext context) throws Exception {
		if (context.getCount() == 0) {
			setupPersonHolders();
			_conversationId = startConversation(_personHolders);
			return this;
		}
		else {
			_conversationId = context.get(String.class, 0);
			_personHolders = getPersonHoldersFromConversation(_conversationId);
			if (_personHolders == null) {
				setupPersonHolders();
				_conversationId = startConversation(_personHolders);
				return this;
			}
		}

		return null;
	}

Also, don't get in the habit of initialising fields in the declaration, eg.

>     private String _conversationId = null;


The reason is that you'll rarely get a freshly instantiated page - more likely is you'll get one that Tapestry has pulled out of its pool of page instances.

Cheers,

Geoff


On 14/03/2010, at 2:11 PM, Greg Pagendam-Turner wrote:

> Guys,
> 
> I'm trying to work out how to fix the AjaxFormLoop example in Jump Start.
> 
> http://jumpstart.doublenegative.com.au:8080/jumpstart/examples/tables/ajaxformloop1
> 
> The code recommends that it should use conversations instead of session
> persistence. I've changed the code to use conversations but still get
> the hidden from field element error when clicking the "Add Row" link as
> mentioned in the JIRA https://issues.apache.org/jira/browse/TAP5-733.
> 
> This is because there is nothing in the rendered partial xml that
> contains a tag that the hidden field can be placed after (such as input,
> select, textarea, label, p, div, td or li).
> 
> Stepping through the code shows that the markup retruned when clicking
> Add Row is just:
> <ajax-partial></ajax-partial>
> 
> I'd appreciate any clues on where to look next please.
> 
> Regards,
> 
> Greg.
> 
> PS: Modified AjaxFormLoop1.java is
> 
> package jumpstart.web.pages.examples.tables;
> 
> import java.text.DateFormat;
> import java.text.Format;
> import java.util.ArrayList;
> import java.util.List;
> import java.util.Locale;
> 
> import jumpstart.business.domain.examples.Person;
> import jumpstart.business.domain.examples.iface.IPersonServiceLocal;
> import jumpstart.client.IBusinessServicesLocator;
> import jumpstart.web.commons.Conversation;
> import jumpstart.web.commons.Conversations;
> import jumpstart.web.commons.ExceptionUtil;
> import jumpstart.web.pages.Index;
> import jumpstart.web.pages.examples.wizard.WizardUsingComponents.Step;
> import jumpstart.web.state.examples.wizard.CreditRequest;
> 
> import org.apache.tapestry5.BindingConstants;
> import org.apache.tapestry5.ComponentResources;
> import org.apache.tapestry5.EventContext;
> import org.apache.tapestry5.ValueEncoder;
> import org.apache.tapestry5.annotations.Component;
> import org.apache.tapestry5.annotations.InjectPage;
> import org.apache.tapestry5.annotations.Parameter;
> import org.apache.tapestry5.annotations.Persist;
> import org.apache.tapestry5.annotations.Property;
> import org.apache.tapestry5.annotations.SessionState;
> import org.apache.tapestry5.corelib.components.Form;
> import org.apache.tapestry5.ioc.annotations.Inject;
> 
> public class AjaxFormLoop1 {
>     private String _conversationId = null;
> 
>     @SessionState
>     private Conversations _conversations;
> 
>     // We've used "session" persistence but it is risky. Better
> techniques include wrapping this field in a
>     // Conversation (see the Wizard examples) or persisting it in the
> database.
>     @Parameter(defaultPrefix = BindingConstants.PROP)
>     @Property
>     //@Persist
>     private List<PersonHolder>  _personHolders;
> 
>     @SuppressWarnings("unused")
>     @Property
>     private PersonHolder _personHolder;
> 
>     private List<Person>  _persons;
> 
>     @Component(id = "personsedit")
>     private Form _form;
> 
>     @Inject
>     private IBusinessServicesLocator _businessServicesLocator;
> 
>     @InjectPage
>     private AjaxFormLoop2 _page2;
> 
>     @Inject
>     private Locale _currentLocale;
> 
>     @Inject
>     private ComponentResources _resources;
> 
>     Object[] onPassivate() {
>         return new Object[] { _conversationId };
>     }
> 
>     Object onActivate(EventContext context) throws Exception {
>         if (context.getCount() == 0) {
>             setupPersonHolders();
>             _conversationId = startConversation(_personHolders);
>         }
>         else
>         {
>             _conversationId = context.get(String.class, 0);
>         }
> 
>         _personHolders = getPersonHoldersFromConversation(_conversationId);
> 
>         return null;
>     }
> 
>     void setupPersonHolders()
>     {
>         // Get all persons - ask business service to find them (from
> the database)
>         _persons = getPersonService().findPersons();
> 
>         _personHolders = new ArrayList<PersonHolder>();
>         for (Person person : _persons) {
>             _personHolders.add(new PersonHolder(person, false,
> person.getId()));
>         }
>     }
> 
>     // Form triggers the PREPARE event during form render and form
> submission.
> 
>     void onPrepare() {
>         if (!_form.getHasErrors()) {
>             if (_personHolders == null) {
>                 //setupPersonHolders();
>                 _personHolders =
> getPersonHoldersFromConversation(_conversationId);
>             }
>         }
>     }
> 
>     PersonHolder onAddRow() {
>         // Create a skeleton Person and add it to the displayed list
> with a unique key
>         Person newPerson = new Person();
>         PersonHolder newPersonHolder = new PersonHolder(newPerson,
> true, 0 - System.nanoTime());
>         _personHolders.add(newPersonHolder);
> 
>         return newPersonHolder;
>     }
> 
>     void onRemoveRow(PersonHolder personPlus) {
>         int index = _personHolders.indexOf(personPlus);
>         PersonHolder holder = _personHolders.get(index);
> 
>         // If the person is new, remove them from the list. Else, flag
> them to be deleted from the database.
>         if (holder.isNew()) {
>             _personHolders.remove(personPlus);
>         }
>         else {
>             holder.setDeleted(true);
>         }
>     }
> 
>     void onValidateForm() {
>         List<Person>  personsToCreate = new ArrayList<Person>();
>         List<Person>  personsToChange = new ArrayList<Person>();
>         List<Person>  personsToDelete = new ArrayList<Person>();
> 
>         for (PersonHolder holder : _personHolders) {
>             if (holder.isNew()) {
>                 personsToCreate.add(holder.getPerson());
>             }
>             else if (holder.isDeleted()) {
>                 personsToDelete.add(holder.getPerson());
>             }
>             else {
>                 personsToChange.add(holder.getPerson());
>             }
>         }
> 
>         System.out.println(">>>  personsToCreate = " + personsToCreate);
>         System.out.println(">>>  personsToChange = " + personsToChange);
>         System.out.println(">>>  personsToDelete = " + personsToDelete);
> 
>         try {
>             // In a real application you would persist them to the
> database instead of printing them
>             // getPersonService().bulkEditPersons(personsToCreate,
> personsToChange, personsToDelete);
>         }
>         catch (Exception e) {
>             // Display the cause. In a real system we would try harder
> to get a user-friendly message.
>             _form.recordError(ExceptionUtil.getRootCause(e));
>         }
>     }
> 
>     Object onSuccess() {
>         List<Person>  persons = new ArrayList<Person>();
>         for (PersonHolder holder : _personHolders) {
>             if (!holder.isDeleted()) {
>                 persons.add(holder.getPerson());
>             }
>         }
>         _page2.set(persons);
>         endConversation(_conversationId);
>         _resources.discardPersistentFieldChanges();
>         return _page2;
>     }
> 
>     void onRefresh() {
>         _resources.discardPersistentFieldChanges();
>         onPrepare();
>     }
> 
>     Object onActionFromGoHome() {
>         _resources.discardPersistentFieldChanges();
>         return Index.class;
>     }
> 
>     @SuppressWarnings("unchecked")
>     public ValueEncoder getEncoder() {
>         return new ValueEncoder<PersonHolder>() {
> 
>             public String toClient(PersonHolder value) {
>                 Long key = value.getKey();
>                 return key.toString();
>             }
> 
>             public PersonHolder toValue(String keyAsString) {
>                 Long key = new Long(keyAsString);
>                 for (PersonHolder holder : _personHolders) {
>                     if (holder.getKey().equals(key)) {
>                         return holder;
>                     }
>                 }
>                 throw new IllegalArgumentException("Received key \"" + key
>                         + "\" which has no counterpart in this
> collection: " + _personHolders);
>             }
>         };
>     }
> 
>     public class PersonHolder {
>         private Person _person;
>         private Long _key;
>         private boolean _new;
>         private boolean _deleted;
> 
>         PersonHolder(Person person, boolean newPerson, Long key) {
>             _person = person;
>             _new = newPerson;
>             _key = key;
>         }
> 
>         public Person getPerson() {
>             return _person;
>         }
> 
>         public Long getKey() {
>             return _key;
>         }
> 
>         public boolean isNew() {
>             return _new;
>         }
> 
>         public boolean setDeleted(boolean deleted) {
>             return _deleted = deleted;
>         }
> 
>         public boolean isDeleted() {
>             return _deleted;
>         }
>     }
> 
>     private IPersonServiceLocal getPersonService() {
>         // Use our business services locator to get the EJB3 session
> bean called "PersonServiceLocal".
>         return _businessServicesLocator.getPersonServiceLocal();
>     }
> 
>     public Format getDateFormat() {
>         return DateFormat.getDateInstance(DateFormat.SHORT,
> _currentLocale);
>     }
> 
>     private String startConversation(Object target) {
>         String conversationId = Long.toString(System.currentTimeMillis());
>         _conversations.add(new Conversation(conversationId, target));
>         return conversationId;
>     }
> 
>     private void endConversation(String conversationId) {
>         _conversations.remove(conversationId);
> 
>         // If conversations ASO is now empty then remove it from the
> session
> 
>         if (_conversations.isEmpty()) {
>             _conversations = null;
>         }
>     }
> 
>     private List<PersonHolder>  getPersonHoldersFromConversation(String
> conversationId) {
>         Conversation conversation = _conversations.get(conversationId);
>         if (conversation != null&&  conversation.getTarget() instanceof
> List<?>) {
>             return (List<PersonHolder>) conversation.getTarget();
>         }
>         return null;
>     }
> 
> }
> 
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>