gridtapestrygregorian-calendar

tapestry5.3.7 t:grid not working with xmlgregoriancalendar


Imported ejb entity into tapestry project looks like this its in "generated sorces (jax-ws)"

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.datatype.XMLGregorianCalendar;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "money", propOrder = {
    "moneyAmount",
    "moneyCurrency",
    "moneyDate",
    "moneyFinishDate",
    "moneyIO",
    "moneyId",
    "moneyName",
    "moneyNextDate",
    "moneyNumber",
    "moneyPurpose",
    "moneyRenewal",
    "moneyTs"
})
public class Money {
    protected Double moneyAmount;
    protected String moneyCurrency;
    @XmlSchemaType(name = "dateTime")
    protected XMLGregorianCalendar moneyDate;
    @XmlSchemaType(name = "dateTime")
    protected XMLGregorianCalendar moneyFinishDate;
    protected MoneyIO moneyIO;
    protected Integer moneyId;
    protected String moneyName;
    @XmlSchemaType(name = "dateTime")
    protected XMLGregorianCalendar moneyNextDate;
    protected Integer moneyNumber;
    protected String moneyPurpose;
    protected MoneyNextTimeType moneyRenewal;
    @XmlSchemaType(name = "dateTime")
    protected XMLGregorianCalendar moneyTs;

    // Getters and Setters for above
    ...
}

all XMLGregorianCalendar is Date in ejb module

In tapestry page class:

@ejb
statefullEjbMoney

@Persist
@Property
List<Money> mylist

void onPrepareForRender() {
  mylist=statefullEjbMoney.findAll();
}

In my template I have:

 <table t:type="grid" t:source="mylist"
                   t:rowsPerPage="10" t:pagerPosition="both"
                   t:exclude="moneyId,moneyIO,moneyNextDate,moneyFinishDate,cliClientId"
                   t:reorder="moneyName,moneyPurpose,moneyAmount,moneyCurrency,moneyDate">
                [Grid here]
            </table>

The stacktrace in the glassfish log is:

Render queue error in SetupRender[Expenses:grid.columns]: Bean editor model for logic.ws.Money does not contain a property named 'moneyDate'.
org.apache.tapestry5.ioc.internal.util.TapestryException: Bean editor model for logic.ws.Money does not contain a property named 'moneyDate'. [at classpath:org/apache/tapestry5/corelib/components/Grid.tml, line 6]
    ...
Caused by: org.apache.tapestry5.ioc.util.UnknownValueException: Bean editor model for logic.ws.Money does not contain a property named 'moneyDate'.
    at org.apache.tapestry5.internal.beaneditor.BeanModelImpl.get(BeanModelImpl.java:160)
    at org.apache.tapestry5.internal.beaneditor.BeanModelImpl.reorder(BeanModelImpl.java:223)
    at org.apache.tapestry5.internal.beaneditor.BeanModelUtils.reorder(BeanModelUtils.java:107)
    at org.apache.tapestry5.internal.beaneditor.BeanModelUtils.modify(BeanModelUtils.java:51)
    at org.apache.tapestry5.corelib.components.Grid.getDataModel(Grid.java:523)
    at org.apache.tapestry5.corelib.components.GridColumns.setupRender(GridColumns.java:112)
    at org.apache.tapestry5.corelib.components.GridColumns.setupRender(GridColumns.java)
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl$SetupRenderPhase.invokeComponent(ComponentPageElementImpl.java:179)
    at org.apache.tapestry5.internal.structure.ComponentPageElementImpl$AbstractPhase.invoke(ComponentPageElementImpl.java:138)
    ... 84 more

If anyone has an idea what is wrong please share?


Solution

  • The exception says the Bean Editor Model doesn't have a property called moneyDate, which I've always thought was a bit unclear.

    To explain, Tapestry doesn't know what an XMLGregorianCalendar is, so when Tapestry creates the Bean Model for your grid, it helpfully ignores it.

    Quick Solution

    The quick way to solve this is to provide getters on your Money entity that return standard JDK Calendars, and display these properties in your grid.

    So in your entity:

    public class Money {
      @XmlSchemaType(name = "dateTime")
      protected XMLGregorianCalendar moneyDate;
    
      public Calendar getMoneyDataCalendar() {
        return moneyDate.toGregorianCalendar();
      }
      ...
    }
    

    Then use it in your grid:

    <table t:type="grid" t:source="mylist" t:rowsPerPage="10" t:pagerPosition="both"
      t:exclude="moneyId,moneyIO,moneyNextDate,moneyFinishDate,cliClientId"
      t:reorder="moneyName,moneyPurpose,moneyAmount,moneyCurrency,moneyDateCalendar">
    
      [Grid here] Note 'moneyDate' changed to 'moneyDateCalendar' above [/Grid here]
    
    </table>
    

    Long Solution

    If you plan to display lots of XMLGregorianCalendars then the (more involved) solution is to tell Tapestry about XMLGregorianCalendars and how to display them.

    First tell Tapestry to add XMLGregorianCalendar to the Bean Model by adding this to your Module class:

    @Contribute(DefaultDataTypeAnalyzer.class)
    public static void contributeDefaultDataTypeAnalyzer(MappedConfiguration<Class<?>, String> config) {
      config.add(XMLGregorianCalendar.class, "xmlGregorianCalendar");
    }
    

    Now tell Tapestry how to display the new "xmlGregorianCalendar". Add to your Module class:

    @Contribute(BeanBlockSource.class)
    public static void contributeDisplayBlocksForGrid(Configuration<BeanBlockContribution> config) {
      config.add(new DisplayBlockContribution("xmlGregorianCalendar", "DisplayBlocks", "xmlGregorianCalendar"));
    }
    

    You now have to create a page called DisplayBlocks with a component with an id xmlGregorianCalendar:

    public class DisplayBlocks {
    
      @Inject
      private Locale locale;
    
      @Property
      @Environmental
      private PropertyOutputContext context;
    
      private final DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
    
      public DateFormat getDateFormat(){
        return dateFormat;
      }
    
      public Date getCalendarDate() {
        XMLGregorianCalendar calendar = (XMLGregorianCalendar) context.getPropertyValue();        
        if (calendar == null)
            return null;        
        return calendar.getTime();
      }
    }
    
    <t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
    
      <t:block id="xmlGregorianCalendar">
        <t:output value="calendarDate" format="dateFormat"/>
      </t:block>
    
    </t:container>
    

    For more info look at the Tapestry source code and see how Tapestry configures itself. In particular look at:

    org.apache.tapestry5.services.TapestryModule.contributeDefaultDataTypeAnalyzer()    
    org.apache.tapestry5.services.TapestryModule.provideDefaultBeanBlocks()
    org.apache.tapestry5.corelib.pages.PropertyDisplayBlocks