Menu

Metawidget, JAXB java bean and collections

Users
Simone
2010-11-17
2012-11-23
  • Simone

    Simone - 2010-11-17

    Hello,

    I'm trying to use your wonderful Metawidget, but I've some problem and doubt. I hope you could and want to help me.

    I would like to use MW with java beans created by JAXB and display them with Icefaces.

    I've these problems:

        * In the generated JAXB classes (for example in the attached LdmDetailsMsg) booleans type are converted in Boolean member variables, but instead of having getXXX methods to access to their value they have isXXX methods . So when I try to display them I get this Exception:  javax.el.PropertyNotFoundException: Property 'boleanObj' not readable on type java.lang.Boolean
          How can I bind the member variables to the isXXX methods ?

        * In the LdmMsg I have a member variable which is a List  of LdmDetailsMsg. In order to display it I'm developing a CollectionsWidgetBuilder that extends IceFacesWidgetBuilder. I would like to create a widget that display each LdmDetailsMsg contained in the list into a panelTabSet (something like Airport Destination in the attached LDM_example.jpg), but I would like that the widget inside the panelTab is generated through MW (inspecting the LdmDetailsMsg). How can accomplish this? How can I "link" the parent panelTabSet widget whith each LdmDetailsMsg tab? How can I recall the widget creation for the LdmDetailsMsg in the list within my CollectionsWidgetBuilder  and put them into my panelTabSet .Have you any suggestion?

    I hope Ii've been clear enought ( I don't think so because of my english! :-) ) and you could help me!!

    Thanks a lot,
    Simone

    @XmlRootElement(name = "LdmMsg", namespace = "http://www.iata.org/txm/sitamsgtypes")
    public class LdmMsg {
        @XmlElement(name = "Ldm_Details")
        protected List<LdmDetailsMsg> ldmDetails;
        @XmlAttribute(name = "Status", required = true)
        protected String status;
        @XmlAttribute(name = "Callsign", required = true)
        protected String callsign;
        @XmlAttribute(name = "Date_Departure")
        protected String dateDeparture;
        @XmlAttribute(name = "Aircraft_Registration", required = true)
        protected String aircraftRegistration;
        @XmlAttribute(name = "Aircraft_Version", required = true)
        protected String aircraftVersion;
        @XmlAttribute(name = "Conf_Crew")
        protected String confCrew;
        @XmlAttribute(name = "Flight_Type")
        protected String flightType;
        @XmlAttribute(name = "Ldm_Status")
        protected String ldmStatus;
        @XmlAttribute(name = "Received_Date")
        protected String receivedDate;
        /**
         * Gets the value of the ldmDetails property.
         * 
         * <p>
         * This accessor method returns a reference to the live list,
         * not a snapshot. Therefore any modification you make to the
         * returned list will be present inside the JAXB object.
         * This is why there is not a <CODE>set</CODE> method for the ldmDetails property.
         * 
         * <p>
         * For example, to add a new item, do as follows:
         * <pre>
         *    getLdmDetails().add(newItem);
         * </pre>
         * 
         * 
         * <p>
         * Objects of the following type(s) are allowed in the list
         * {@link LdmDetailsMsg }
         * 
         * 
         */
        public List<LdmDetailsMsg> getLdmDetails() {
            if (ldmDetails == null) {
                ldmDetails = new ArrayList<LdmDetailsMsg>();
            }
            return this.ldmDetails;
        }
      [...]
    }
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "LdmDetailsMsg", namespace = "http://www.iata.org/txm/sitamsgtypes")
    public class LdmDetailsMsg {
        @XmlAttribute(name = "Dest_Airport", required = true)
        protected String destAirport;
        @XmlAttribute(name = "Nil")
        protected Boolean nil;
        @XmlAttribute(name = "Passengers_Number", required = true)
        protected String passengersNumber;
        @XmlAttribute(name = "Cabin_Baggage")
        protected String cabinBaggage;
        @XmlAttribute(name = "Total_Deadload")
        protected String totalDeadload;
        @XmlAttribute(name = "Total_Weight_Load")
        protected String totalWeightLoad;
        @XmlAttribute(name = "Compartment_Load", required = true)
        protected String compartmentLoad;
        @XmlAttribute(name = "Pax")
        protected String pax;
        @XmlAttribute(name = "Pad")
        protected String pad;
        @XmlAttribute(name = "Remark")
        protected String remark;
        /**
         * Gets the value of the destAirport property.
         * 
         * @return
         *     possible object is
         *     {@link String }
         *     
         */
        public String getDestAirport() {
            return destAirport;
        }
        /**
         * Sets the value of the destAirport property.
         * 
         * @param value
         *     allowed object is
         *     {@link String }
         *     
         */
        public void setDestAirport(String value) {
            this.destAirport = value;
        }
        /**
         * Gets the value of the nil property.
         * 
         * @return
         *     possible object is
         *     {@link Boolean }
         *     
         */
        public Boolean isNil() {
            return nil;
        }
        /**
         * Sets the value of the nil property.
         * 
         * @param value
         *     allowed object is
         *     {@link Boolean }
         *     
         */
        public void setNil(Boolean value) {
            this.nil = value;
        }
    [img]http://img528.imageshack.us/img528/5674/ldmexample.jpg[/img]
    
     
  • Kennard Consulting

    Simone,

    Thank you for your interest in Metawidget.

    If it's okay with you, I'd like to answer your questions one at a time, in case we end up with multiple conversations in the same thread, which gets very confusing! So:

    1. The error you are getting (javax.el.ProperyNotFoundException) appears to be coming from JSF (javax.el) not Metawidget? This is possibly because Boolean (big 'B') getters are meant to be 'get', not 'is'. Only boolean (little 'b') getters are meant to be 'is'. Can you please confirm this? Can you try a simple…

    <h:inputText value="#{myLdmDetailsMsg.nil}"/>
    

    …without Metawidget, and see if that works?

    Then we can look at your second question.

    Regards,

    Richard.

     
  • Simone

    Simone - 2010-11-18

    Hi Richard,
    Thanks a lot!
    I'll try it as soon as possible and I'll let you know the results !!

    Simone

     
  • Simone

    Simone - 2010-11-18

    Richard,

    you are right! The error is due to JAXB that creates  isXXX access methods instead of the getXXX for Boolean object.
    Now I have to find the way to manage this, but this is a problem between me and JAXB! :-)

    Thanks again!

    PS: I'm trying your code for the secodn question!

     
  • Simone

    Simone - 2010-11-23

    Hi Richard,
    I'm doing progress!

    http://img713.imageshack.us/img713/6469/ldmexample2.jpg

    http://img16.imageshack.us/img16/2295/ldmexample3.jpg

    I've resolved the first problem and now JAXB correctly create getter Methods for Boolean object ( I needed to upgrade to JAXB version 2.2 and use the -enableIntrospection flag).

    I've also added an xmlAnnotationInspector to extract XmlAttribute information from JAXB java beans ( I needed also to implement an XmlJavaBeanPropertyStyle to inspect protected members variables).

    Now I'm facing another problem: if  I set the COMPONENT_ATTRIBUTE_NOT_RECREATABLE attribute to panelTabSet, when I switch to another bean from the combo I get an exception :

    javax.servlet.ServletException: java.io.IOException: javax.el.PropertyNotFoundException: Property 'ldmDetails' not found on type it.trs.atc.ahs.gcm.sita.decoder.BTMMsg
        com.icesoft.faces.webapp.http.servlet.MainServlet.service(MainServlet.java:158)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
        com.icesoft.faces.webapp.xmlhttp.BlockingServlet.service(BlockingServlet.java:56)
    

    If I've understood right, it is because the panelTabSet is not destroyed and so it try to set the ldmDetails property also for the new selected BTMMsg bean. Right?

    On the other hand, if I don't set the attibute COMPONENT_ATTRIBUTE_NOT_RECREATABLE I cannot switch between tabs (as you said to me) because the panelTabSet lost its state.
    Have you any suggestion ?!?

    Thanks a lot again !

    Simone

     
  • Simone

    Simone - 2010-11-30

    Hi Richard,

    this is the result I 've obtained http://img522.imageshack.us/img522/6750/ldmexample4.jpg.

    I've not yet implemented your last improvement (I've not much time for now), but I'm able to add and remove tabs on the fly using java reflection for the creation of a new object of the list in the onAdd method, getting its type from the PARAMETERIZED_TYPE attribute.

    This is the code of my IceFacesTabsWidgetBuilder:

    public class IceFacesTabsWidgetBuilder implements WidgetBuilder < UIComponent , UIMetawidget > {
        private static Log logger = LogFactory.getLog(IceFacesTabsWidgetBuilder.class );
        //
        // Public methods
        //
        @Override
        public UIComponent buildWidget(String elementName, Map < String , String > attributes, UIMetawidget metawidget ) {
            // Not for us?
            String type = attributes.get(TYPE );
            if ( type == null ) {
                return null;
            }
            Class < ? > clazz = ClassUtils.niceForName(type );
            if ( !List.class.isAssignableFrom(clazz ) ) {
                return null;
            }
            final UIMetawidget finalWidget = metawidget;
            final String paramType = attributes.get(PARAMETERIZED_TYPE );
            logger.debug("\n\n\n**************THIS IS A LIST!****************************************" );
            logger.debug("elementName \t= " + elementName );
            logger.debug("metawidget \t= " + metawidget );
            logger.debug("attributes \t= " + attributes );
            Set < String > keys = attributes.keySet();
            for ( String item : keys ) {
                logger.debug("  key=" + item + " \t\t--- value=" + attributes.get(item ) );
            }
            logger.debug("" );
            // Create a Stub to wrap around the PanelTabSet.
            //
            // This is because:
            //
            // 1. PanelTabSet will not switch tabs if it is destroyed/recreated. We must set
            // COMPONENT_ATTRIBUTE_NOT_RECREATABLE on it (see
            // http://metawidget.sourceforge.net/doc/reference/en/html/ch02s04.html#section-architecture-widgetbuilders-implementing-your-own-faces).
            // This limits some dynamicism, such as being able to dynamically add tabs, but there are
            // further ways around that
            //
            // 2. COMPONENT_ATTRIBUTE_NOT_RECREATABLE only works if there is a 'value' expression that
            // OverriddenWidgetBuilder can match against
            //
            // 3. PanelTabSet is a little unusual in how it treats its 'value' expression (binding it to
            // a UI mechanism, rather than a model mechanism) so we need to avoid using it. We use the
            // Stub's 'value' expression instead
            //
            UIStub stub = new UIStub();
            FacesContext context = FacesContext.getCurrentInstance();
            final ELContext elContext = context.getELContext();
            final ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory();
            // retrieve the variable binded to the metawidget in the .jspx file
            // (in this case "#{iataSentDetails.objSelected}" in iataSentDetails.jspx)
            ValueExpression metawidgetValueExpression = metawidget.getValueExpression("value" );
            String metawidgetValueExpressionString = FacesUtils.unwrapExpression(metawidgetValueExpression
                    .getExpressionString() );
            logger.debug("metawidgetValueExpression=" + metawidgetValueExpression );
            logger.debug("metawidgetValueExpressionString=" + metawidgetValueExpressionString );
            // this is the List<?> variable (in this case #{iataSentDetails.objSelected.ldmDetails})
            // for which we have to create the PanelTabSet metawidget to return
            final String itemsListValueExpressionString = metawidgetValueExpressionString + StringUtils.SEPARATOR_DOT_CHAR
                    + attributes.get(NAME );
            final ValueExpression itemsListValueExpression = expressionFactory.createValueExpression(elContext,
                    FacesUtils.wrapExpression(itemsListValueExpressionString ), Object.class );
            logger.debug("itemsListValueExpression=" + itemsListValueExpression );
            logger.debug("itemsListValueExpressionString=" + itemsListValueExpressionString );
            // bind the value to the stub
            stub.setValueExpression("value", itemsListValueExpression );
            // Create tab set
            final PanelTabSet panelTabSet = new PanelTabSet();
            // panelTabSet.setPartialSubmit(partialSubmit );
            panelTabSet.getAttributes().put(UIMetawidget.COMPONENT_ATTRIBUTE_NOT_RECREATABLE, true );
            final List < UIComponent > tabsContainer = panelTabSet.getChildren();
            // Lookup the List from the parent Metawidget to see how many items it has
            // questa e' proprio la lista che abbiamo associato prima (ad es.
            // iataSentDetails.objSelected.ldmDetails )
            final List itemsList = (List ) itemsListValueExpression.getValue(elContext );
            createTabs(itemsList, tabsContainer, itemsListValueExpressionString, finalWidget );
            // Add a button to create a new item of the list
            HtmlCommandButton addButton = new HtmlCommandButton();
            addButton.setValue("Add" );
            addButton.addActionListener(new ActionListener() {
                @Override
                public void processAction(ActionEvent event ) throws AbortProcessingException {
                    logger.debug("ADD ACTION =" + event );
                    onAdd(itemsList, paramType, tabsContainer, itemsListValueExpressionString, finalWidget );
                }
            } );
            addButton.getAttributes().put(UIMetawidget.COMPONENT_ATTRIBUTE_NOT_RECREATABLE, true );
            addButton.setImmediate(true );
            // Add a button to remove the selected item of the list
            HtmlCommandButton delButton = new HtmlCommandButton();
            delButton.setValue("Del" );
            delButton.addActionListener(new ActionListener() {
                @Override
                public void processAction(ActionEvent event ) throws AbortProcessingException {
                    logger.debug("DEL ACTION=" + event );
                    onDel(itemsList, panelTabSet, tabsContainer, itemsListValueExpressionString, finalWidget );
                }
            } );
            delButton.getAttributes().put(UIMetawidget.COMPONENT_ATTRIBUTE_NOT_RECREATABLE, true );
            delButton.setImmediate(true );
            stub.getChildren().add(addButton );
            stub.getChildren().add(delButton );
            stub.getChildren().add(panelTabSet );
            // Return tab set
            logger.debug("component =" + stub );
            logger.debug("**************END LIST TAB CREATION !****************************************\n\n\n" );
            return stub;
        }
        /**
         * @param list
         * @param tabs
         * @param tabSetValueExpressionString
         * @param finalWidget
         */
        private void createTabs(List itemsList, List < UIComponent > tabsContainer, String itemsListValueExpressionString,
                UIMetawidget finalWidget ) {
            // clear the tabs metawidget and recreate them (in order to avoid index problem on deletion)
            tabsContainer.clear();
            // Create one tab per item...
            for ( int i = 0, length = itemsList.size(); i < length; i++ ) {
                PanelTab panelTab = createNewPanelTab(i, itemsListValueExpressionString, finalWidget );
                tabsContainer.add(panelTab );
            }
        }
        /**
         * Add a new metawidget for the item list
         * @param index index of the item in the list
         * @param itemsListValueExpressionString
         * @param finalWidget
         */
        private PanelTab createNewPanelTab(int index, String itemsListValueExpressionString, UIMetawidget finalWidget ) {
            FacesContext context = FacesContext.getCurrentInstance();
            final ELContext elContext = context.getELContext();
            final ExpressionFactory expressionFactory = context.getApplication().getExpressionFactory();
            PanelTab panelTab = new PanelTab();
            panelTab.setTitle("Airport " + ( index + 1 ) );
            panelTab.setLabel("Airport " + ( index + 1 ) );
            // ...with each tab itself containing a Metawidget.
            //
            // The 'value' expression of each Metawidget uses the EL #{foo.bar[0]} notation for
            // accessing arrays
            HtmlMetawidget tabMetawidget = new HtmlMetawidget();
            // binding of the actual indexed item of the list
            final ValueExpression actualItemListValueExpression = expressionFactory.createValueExpression(elContext,
                    FacesUtils.wrapExpression(itemsListValueExpressionString + '[' + index + ']' ), Object.class );
            logger.debug("actualItemListValueExpression=" + actualItemListValueExpression );
            logger.debug("actualItemListValueExpressionString=" + actualItemListValueExpression.getExpressionString() );
            // associo l'i-esimo item al value della tab
            tabMetawidget.setValueExpression("value", actualItemListValueExpression );
            tabMetawidget.setValueExpression("readOnly", finalWidget.getValueExpression("readOnly" ) );
            panelTab.getChildren().add(tabMetawidget );
            return panelTab;
        }
        /**
         * @param itemsList
         * @param paramType
         * @param tabsContainer
         * @param itemsListValueExpressionString
         * @param finalWidget
         * @throws AbortProcessingException
         */
        private void onAdd(List itemsList, String paramType, List < UIComponent > tabsContainer,
                String itemsListValueExpressionString, UIMetawidget finalWidget ) throws AbortProcessingException {
            try {
                logger.debug("initial list size=" + itemsList.size() );
                logger.debug("paramType=" + paramType );
                Class c = Class.forName(paramType );
                Constructor constructor = c.getConstructor();
                Object newItem = constructor.newInstance();
                itemsList.add(newItem );
                int index = itemsList.indexOf(newItem );
                logger.debug("Item added " + newItem );
                logger.debug("list size=" + itemsList.size() );
                // add the new item
                PanelTab panelTab = createNewPanelTab(index, itemsListValueExpressionString, finalWidget );
                tabsContainer.add(panelTab );
            } catch ( Exception e ) {
                logger.error(e.getMessage(), e );
                throw new AbortProcessingException(e.getMessage(), e );
            }
        }
        /**
         * 
         * @param itemsList
         * @param panelTabSet
         * @param tabsContainer
         * @param itemsListValueExpressionString
         * @param finalWidget
         * @throws AbortProcessingException
         */
        private void onDel(List itemsList, PanelTabSet panelTabSet, List < UIComponent > tabsContainer,
                String itemsListValueExpressionString, UIMetawidget finalWidget ) throws AbortProcessingException {
            try {
                FacesContext context = FacesContext.getCurrentInstance();
                final ELContext elContext = context.getELContext();
                logger.debug("initial list size=" + itemsList.size() );
                int selectedTab = panelTabSet.getSelectedIndex();
                logger.debug("selectedPanelTab=" + selectedTab );
                UIComponent removedPanelTab = tabsContainer.remove(selectedTab );
                logger.debug("removedPanelTab =" + removedPanelTab );
                UIComponent tabMetawidget = removedPanelTab.getChildren().get(0 );
                logger.debug("removedTabMetawidget =" + tabMetawidget );
                ValueExpression tabValueExpression = tabMetawidget.getValueExpression("value" );
                logger.debug("tabValueExpression=" + tabValueExpression );
                Object removedObject = tabValueExpression.getValue(elContext );
                logger.debug("removedObject=" + removedObject );
                itemsList.remove(removedObject );
                logger.debug("final list size=" + itemsList.size() );
                //recreate tabs of the updated list.
                createTabs(itemsList, tabsContainer, itemsListValueExpressionString, finalWidget );
                if ( selectedTab > 0 ) {
                    panelTabSet.setSelectedIndex(selectedTab - 1 );
                }
            } catch ( Exception e ) {
                logger.error(e.getMessage(), e );
                throw new AbortProcessingException(e.getMessage(), e );
            }
        }
    }
    

    In order to retrieve the information of the XmlAttribute annotation I've implemented an XmlAnnotationInspector, an XmlAnnotationConfig and an XmlJavaBeanPropertyStyle that can read them from protected members:

    public class XmlAnnotationInspector extends BaseObjectInspector{
        private static Log logger = LogFactory.getLog(XmlAnnotationInspector.class );
        /**
         * 
         */
        public XmlAnnotationInspector() {
            this( new XmlAnnotationConfig() );
        }
        
        public XmlAnnotationInspector( BaseObjectInspectorConfig config ) {
            super( config );
        }
        @Override
        protected Map < String , String > inspectProperty(Property property ) throws Exception {
            
            Map < String , String > attributes = CollectionUtils.newHashMap(); 
            
            XmlAttribute xmlAttrib =  property.getAnnotation(XmlAttribute.class );
            
            if(xmlAttrib != null && xmlAttrib.required()){
                attributes.put( REQUIRED, TRUE );
            }
            
            return attributes;
        }
           
    }
    
    public class XmlAnnotationConfig extends BaseObjectInspectorConfig {
        /**
         * 
         */
        public XmlAnnotationConfig() {
            //set the specific property style
            setPropertyStyle(new XmlJavaBeanPropertyStyle() );
        }
        
    }
    
    public class XmlJavaBeanPropertyStyle extends JavaBeanPropertyStyle{
        
        @Override
        protected Map<String, Property> inspectProperties( Class<?> clazz ) {
            // TreeMap so that returns alphabetically sorted properties
            Map<String, Property> properties = CollectionUtils.newTreeMap();
            // Lookup fields, getters and setters
            lookupFields( properties, clazz );
            return properties;
        }
        
        /**
         * Lookup all field-based properties.
         * <p>
         * This method will be called before <code>lookupGetters</code> and <code>lookupSetters</code>.
         */
        @Override
        protected void lookupFields( Map<String, Property> properties, Class<?> clazz ) {
            // Note: we must use clazz.getFields(), not clazz.getDeclaredFields(), in order
            // to avoid Applet SecurityExceptions
            for ( Field field : clazz.getDeclaredFields() ) {
                // Exclude static fields
                int modifiers = field.getModifiers();
                if ( Modifier.isStatic( modifiers ) || Modifier.isPrivate(modifiers )) {
                    continue;
                }
                // Get name and type
                String fieldName = field.getName();
                Class<?> type = field.getType();
                // Exclude based on other criteria
                if ( isExcluded( field.getDeclaringClass(), fieldName, type ) ) {
                    continue;
                }
                properties.put( fieldName, new FieldProperty( fieldName, field ) );
            }
        }
    }
    

    I hope this could be useful !!!

    Thanks again for your help,

    Simone

     
  • Kennard Consulting

    Simone,

    Very interesting! Thanks for posting your work.

    I have added a feature to Metawidget v1.05…

    http://kennardconsulting.blogspot.com/2010/10/javabean-convention-relating-public.html

    …that may remove the need for your custom XmlJavaBeanPropertyStyle. Can you see if it would meet your needs?

    Regards,

    Richard.

     
  • Kennard Consulting

    Simone,

    Metawidget v1.05 now released, including the new support for annotating private fields. I'd be most grateful if you could download it and confirm it works for you (removes the need for your XmlJavaBeanPropertyStyle).

    Regards,

    Richard.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.