user3027786
user3027786

Reputation: 185

JSF throws class does not have the property for DynamicEntity

I want to use a DynamicEntity as a managed bean providing properties for my xhtml form.

When I use the above as backing bean for my xhtml page, I got the following exception:

The class 'com.invoice.Invoic' does not have the property 'sellerAddress'.
com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:111)
javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:182)
javax.faces.component.UIOutput.getValue(UIOutput.java:169)
com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:205)
com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:355)
com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:164)
javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:875)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1786)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782)
javax.faces.component.UIComponent.encodeAll(UIComponent.java:1782)
com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:402)
com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:125)
javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288)
org.ocpsoft.rewrite.faces.RewriteViewHandler.renderView(RewriteViewHandler.java:186)
javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288)
com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121)
com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
org.ocpsoft.rewrite.servlet.RewriteFilter.doFilter(RewriteFilter.java:200)
com.tsystems.odx.idm.authentication.web.AuthenticationFilter.doFilter(AuthenticationFilter.java:231)
org.ocpsoft.rewrite.servlet.impl.HttpRewriteResultHandler.handleResult(HttpRewriteResultHandler.java:38)
org.ocpsoft.rewrite.servlet.RewriteFilter.rewrite(RewriteFilter.java:263)
org.ocpsoft.rewrite.servlet.RewriteFilter.doFilter(RewriteFilter.java:188)
com.tsystems.odx.idm.authentication.web.AuthenticationFilter.doFilter(AuthenticationFilter

I put a debug breakpoint and looked into my DynamicEntity object, the sellerAddress object was there.

Here is a dummy example how I would like to use DynamicEntity as bean

package com.test.beans;

import java.io.InputStream;

import javax.annotation.PostConstruct;
import javax.enterprise.inject.Produces;
import javax.faces.bean.ViewScoped;
import javax.inject.Named;
import javax.xml.bind.JAXBException;

import org.eclipse.persistence.dynamic.DynamicEntity;
import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext;
import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory;

@Named
@ViewScoped
public class CustomerHandler {

    DynamicJAXBContext jaxbContext;

    @PostConstruct
    public void init() throws JAXBException {
        InputStream xsdInputStream = getClass().getClassLoader().getResourceAsStream("customer.xsd");
        jaxbContext = DynamicJAXBContextFactory.createContextFromXSD(xsdInputStream, null, null,
                null);
    }

    @Produces
    @Named("Customer")
    public DynamicEntity createCustomer() {
        DynamicEntity customer = jaxbContext.newDynamicEntity("org.example.Customer");
        customer.set("name", "Jane Doe");
        DynamicEntity address = jaxbContext.newDynamicEntity("org.example.Address");
        address.set("street", "1 Any Street").set("city", "Any Town");
        customer.set("address", address);
        return customer;
    }

    public void save()
    {
        // do something
    }
}

The customer.xsd file is in src/main/resources

<xsd:schema 
   xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
   xmlns="http://www.example.org" 
   targetNamespace="http://www.example.org"
   elementFormDefault="qualified">

   <xsd:complexType name="address">
      <xsd:sequence>
         <xsd:element name="street" type="xsd:string" minOccurs="0"/>
         <xsd:element name="city" type="xsd:string" minOccurs="0"/>
      </xsd:sequence>
   </xsd:complexType>

   <xsd:element name="customer">
      <xsd:complexType>
         <xsd:sequence>
            <xsd:element name="name" type="xsd:string" minOccurs="0"/>
            <xsd:element name="address" type="address" minOccurs="0"/>
         </xsd:sequence>
      </xsd:complexType>
   </xsd:element>

</xsd:schema>

And my test page is customer.xhtml

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">

<h:head>
    <title>Welcome to Customer Page</title>
</h:head>

<h:body>
    <h:form>
        <h:inputText value="#{Customer.name}" />
        <h:inputText value="#{Customer.address.street}" />
        <h:commandButton value="Submit" action="#{customerHandler.save()}" />
    </h:form>   
</h:body>
</html>

Upvotes: 0

Views: 894

Answers (1)

skuntsel
skuntsel

Reputation: 11742

Documentation for DynamicEntity class shows us that it is an interface and nowhere states that this interface provides for good old POJO getters like getSellerAddress() that will return the desired property. As you're trying to manipulate the values as #{Customer.name} that is indeed not going to work, as there is no getName()/setName() method pair.

The methods exposed by the DynamicEntity interfaces are the following:

  • DynamicEntity#get(propertyName) to retrieve the value and
  • DynamicEntity#set(propertyName, value) to preset the value.

So there is clearly no way to use these methods to create a bilateral binding with a bean property in a standard JSF way.


You have the following choices to deal with your problem:

  1. Switch to JPA and create real entities like @Entity public class Customer as your model. This is clearly the best way to go further and the best practice;
  2. Create a placeholder/backing bean to a JSF form to keep the submitted values and create your dynamic entity in the action method for further manipulation. Be aware that you will miss all the points of JSF if you decide to follow this route;
  3. Most probably the implementation of DynamicEntity (that is, the instance returned from the call to DynamicJAXBContext#newDynamicEntity(String n)) is backed by a Map implementation. Debug to see what that call returns and apply that information to your use case. In case it's backed by a map with a public getter/setter pair with the name persistentMap your EL will become #{Customer.persistentMap['name']. Be aware that this is a discouraged practice and an example of tight coupling and is possible in case there are accessors for the backed collection.

To sum it up, I'd highly recommended to switch to JPA to solve your use case.

Upvotes: 1

Related Questions