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-unsubscr...@tapestry.apache.org
> For additional commands, e-mail: users-h...@tapestry.apache.org
> 

Reply via email to