[jboss-user] [EJB 3.0] - Returning a class with OneToMany mappings causes infinite XM

mjhammel do-not-reply at jboss.com
Mon Jan 7 18:35:46 EST 2008


I've managed to create my first Web Services interface that retrieves a record from a MySQL db via an EJB3.0 bean generated by Hibernate using reverse engineering.  However, I can't return this object to the remote client because I get the following error:

15:22:47,324 INFO  [STDOUT] Subscriber.find(): em is not null; returning subscriber for guid = CRUNCH-DEFAULT-SUPERUSER
  | 15:22:47,339 INFO  [STDOUT] Count           : 7
  | 15:22:47,339 INFO  [STDOUT] Subscriber name : SuperUser
  | 15:22:47,340 INFO  [STDOUT] Subscriber email: admin at localhost
  | 15:22:47,406 ERROR [RequestHandlerImpl] Error processing web service request
  | org.jboss.ws.WSException: javax.xml.ws.WebServiceException: javax.xml.bind.MarshalException
  |  - with linked exception:
  | [com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: com.cei.crunch.ejb.Subscriber at 1e51549 -> com.cei.crunch.ejb.Crunchroles at 538af7 -> com.cei.crunch.ejb.Subscriber at 1e51549]

It seems that if your returned object includes a OneToMany mapping you get this problem.  I can reference the object on the server side and return individual fields from it to the remote client, but I can't return the whole object.  Since the class is being automatically generated with Hibernate, I'm wondering how I can return any db objects to callers?    In fact, what if I wanted to create a new class, one that doesn't represent a table in the db, and return that?  How does the client reference this class?  Note that I'm using wsconsume to generate the client side classes used to interact with the Web Service.  I know this latter is a WS question and not an EJB question, but my initial problem still stands - what can I do to return this EJB that has OneToMany mappings in it without getting the infinite XML error?

This is what my Web Services/Session Bean looks like:

package com.cei.crunch.server.ws.SubscriberServices;
  | 
  | import javax.ejb.*;
  | import javax.jws.WebService;
  | import javax.jws.WebMethod;
  | import javax.jws.soap.SOAPBinding;
  | import javax.persistence.*;
  | import javax.naming.InitialContext;
  | import javax.transaction.UserTransaction;
  | import javax.annotation.Resource;
  | 
  | import com.cei.crunch.ejb.Subscriber;
  | import com.cei.crunch.ejb.SubscriberHome;
  | 
  | /* Make this an EJB3 service endpoint. */
  | @Stateless
  | @Remote(SubscriberServices.class)
  | 
  | /* Make this an Web Services endpoint. */
  | @WebService(
  |     name = "SubscriberWS",
  |     endpointInterface = "com.cei.crunch.server.ws.SubscriberServices.SubscriberServicesEndpoint"
  |     )
  | @SOAPBinding(style = SOAPBinding.Style.RPC)
  | 
  | /**
  |  * The .Crunch interface to Subscriber Services
  |  */
  | public class SubscriberServices implements SubscriberServicesEndpoint {
  | 
  |     private UserTransaction utx;
  |     private EntityManager em;
  | 
  |     @WebMethod
  |     public String findSubscriber(String guid)
  |     {
  |         Subscriber s = null;
  | 
  |         InitialContext ctx = null;
  |         EntityManager em = null;
  |         try {
  |             ctx = new InitialContext();
  |             em =  (EntityManager) ctx.lookup("java:/CrunchPersistence");
  |             utx = (UserTransaction) ctx.lookup("java:/comp/UserTransaction");
  |         }
  |         catch (Exception e)
  |         {
  |             // throw new WebServiceException(ex);
  |             System.out.println("Subscriber.find(): context lookup failure.");
  |             e.printStackTrace();
  |             return null;
  |         }
  | 
  |         if ( em == null )
  |         {
  |             System.out.println("Subscriber.find(): em is null; can't find subscriber");
  |         }
  |         else
  |         {
  |             System.out.println("Subscriber.find(): em is not null; returning subscriber for guid = " + guid);
  |             try {
  |                 utx.begin();
  |                 s = em.find(Subscriber.class, guid);
  |                 int count = s.getCrunchroleses().size();
  |                 if ( s != null )
  |                 {
  |                     System.out.println("Count           : " + count);
  |                     System.out.println("Subscriber name : " + s.getFirstname());
  |                     System.out.println("Subscriber email: " + s.getEmailAddress());
  |                 }
  |                 utx.commit();
  |             }
  |             catch (Exception e)
  |             {
  |                 System.out.println("Subscriber.find(): find problem.");
  |                 e.printStackTrace();
  |             }
  |         }
  |         if ( s != null )
  |             return s.getGuid();
  |         else
  |             return null;
  |     }
  | 
  | }

Some notes on this.  First, injection is not working under Web Services in 4.2.2GA (see my previous posts in the WS forum - http://www.jboss.com/index.html?module=bb&op=viewtopic&t=126927) so the context and transaction have to be created manually.  Second, if you don't reference the size of one of the Set's in this EJB before referencing the fields you get LazyInitializationException errors.  

The Subscriber table looks like this - note that there are no foreign key references here, but other tables have foreign key references back to this table which causes the EJB for this table (as generated by Hibernate) to include the foreign key methods.

create table Subscriber (
  |   guid              varchar(50) not null,  
  |   Username          varchar(256),           -- The login id for this subscriber
  |   password          varchar(256),           -- The password for this subscriber; will need encryption set for this
  | 
  |   addr1             varchar(256),           -- Home address
  |   addr2             varchar(256),           -- Home address
  |   city              varchar(256),           -- City or Town of origin
  |   province          varchar(256),           -- State or Province 
  | 
  |   country           varchar(256),           -- 
  |   postalCode        varchar(50),            -- 
  |   emailAddress      varchar(1024),          --
  |   phone             varchar(50),            -- 
  |   latitude          float,                  -- Used only to place subscribers on a map
  |   longitude         float,                  -- Used only to place subscribers on a map
  | 
  |   firstname         varchar(256),           -- The subscriber first name
  |   lastname          varchar(256),           -- The subscriber last name 
  |   organizationGuid  varchar(50),            -- The organization this subscriber belongs to, if any
  |   groupGuid         varchar(50),            -- The group within the organization, if any 
  |   teamGuid          varchar(50),            -- The team the subscriber chose, if any
  |   viewLevelGuid     varchar(50),            -- The view level this subscriber has
  |   viewAccessGuid    varchar(50),            -- The view level this subscriber can see
  |   lastLogin         varchar(50),            -- When the subscriber last logged in
  |   state             int,                    -- 0: new, 1: active, 2: inactive, 3: deleted, 4: banned
  |   priority          int,                    -- Higher values have higher priority
  |   
  |   PRIMARY KEY(guid)
  | ) 

The EJB generated by hbm2java looks like this:

package com.cei.crunch.ejb;
  | // Generated Jan 7, 2008 3:51:39 PM by Hibernate Tools 3.2.0.CR1
  | 
  | 
  | import java.util.HashSet;^M
  | import java.util.Set;^M
  | import javax.persistence.Column;^M
  | import javax.persistence.Entity;^M
  | import javax.persistence.FetchType;^M
  | import javax.persistence.Id;^M
  | import javax.persistence.OneToMany;^M
  | import javax.persistence.Table;^M
  | 
  | /**
  |  * Subscriber generated by hbm2java
  |  */
  | @Entity
  | @Table(name="subscriber"
  |     ,catalog="crunch"
  | )
  | public class Subscriber  implements java.io.Serializable {
  | 
  | 
  |      private String guid;
  |      private String username;
  |      private String password;
  |      private String addr1;
  |      private String addr2;
  |      private String city;
  |      private String province;
  |      private String country;
  |      private String postalCode;
  |      private String emailAddress;
  |      private String phone;
  |      private Float latitude;
  |      private Float longitude;
  |      private String firstname;
  |      private String lastname;
  |      private String organizationGuid;
  |      private String groupGuid;
  |      private String teamGuid;
  |      private String viewLevelGuid;
  |      private String viewAccessGuid;
  |      private String lastLogin;
  |      private Integer state;
  |      private Integer priority;
  |      private Set<Subscribersession> subscribersessions = new HashSet<Subscribersession>(0);
  |      private Set<Crunchroles> crunchroleses = new HashSet<Crunchroles>(0);
  | 
  | 
  |     public Subscriber() {
  |     }
  | 
  | 
  |     public Subscriber(String guid) {
  |         this.guid = guid;
  |     }
  |     public Subscriber(String guid, String username, String password, String addr1, String addr2, String city, String province, String country, String postalCode, String emailAddress, String phone, Float latitude, Float longitude, String firstname, String lastname, String organizationGuid, String groupGuid, String teamGuid, String viewLevelGuid, String viewAccessGuid, String lastLogin, Integer state, Integer priority, Set<Subscribersession> subscribersessions, Set<Crunchroles> crunchroleses) {
  |        this.guid = guid;
  |        this.username = username;
  |        this.password = password;
  |        this.addr1 = addr1;
  |        this.addr2 = addr2;
  |        this.city = city;
  |        this.province = province;
  |        this.country = country;
  |        this.postalCode = postalCode;
  |        this.emailAddress = emailAddress;
  |        this.phone = phone;
  |        this.latitude = latitude;
  |        this.longitude = longitude;
  |        this.firstname = firstname;
  |        this.lastname = lastname;
  |        this.organizationGuid = organizationGuid;
  |        this.groupGuid = groupGuid;
  |        this.teamGuid = teamGuid;
  |        this.viewLevelGuid = viewLevelGuid;
  |        this.viewAccessGuid = viewAccessGuid;
  |        this.lastLogin = lastLogin;
  |        this.state = state;
  |        this.priority = priority;
  |        this.subscribersessions = subscribersessions;
  |        this.crunchroleses = crunchroleses;
  |     }
  | 
  |      @Id
  | 
  |     @Column(name="guid", nullable=false, length=50)
  |     public String getGuid() {
  |         return this.guid;
  |     }
  | 
  | 
  |     public void setGuid(String guid) {
  |         this.guid = guid;
  |     }
  | 
  |     @Column(name="Username", length=256)
  |     public String getUsername() {
  |         return this.username;
  |     }
  | 
  |     public void setUsername(String username) {
  |         this.username = username;
  |     }
  | 
  |     @Column(name="password", length=256)
  |     public String getPassword() {
  |         return this.password;
  |     }
  | 
  |     public void setPassword(String password) {
  |         this.password = password;
  |     }
  | 
  |     @Column(name="addr1", length=256)
  |     public String getAddr1() {
  |         return this.addr1;
  |     }
  | 
  |     public void setAddr1(String addr1) {
  |         this.addr1 = addr1;
  |     }
  | 
  |     @Column(name="addr2", length=256)
  |     public String getAddr2() {
  |         return this.addr2;
  |     }
  | 
  |     public void setAddr2(String addr2) {
  |         this.addr2 = addr2;
  |     }
  | 
  |     @Column(name="city", length=256)
  |     public String getCity() {
  |         return this.city;
  |     }
  | 
  |     public void setCity(String city) {
  |         this.city = city;
  |     }
  | 
  |     @Column(name="province", length=256)
  |     public String getProvince() {
  |         return this.province;
  |     }
  | 
  |     public void setProvince(String province) {
  |         this.province = province;
  |     }
  | 
  |     @Column(name="country", length=256)
  |     public String getCountry() {
  |         return this.country;
  |     }
  | 
  |     public void setCountry(String country) {
  |         this.country = country;
  |     }
  | 
  |     @Column(name="postalCode", length=50)
  |     public String getPostalCode() {
  |         return this.postalCode;
  |     }
  | 
  |     public void setPostalCode(String postalCode) {
  |         this.postalCode = postalCode;
  |     }
  | 
  |     @Column(name="emailAddress", length=1024)
  |     public String getEmailAddress() {
  |         return this.emailAddress;
  |     }
  | 
  |     public void setEmailAddress(String emailAddress) {
  |         this.emailAddress = emailAddress;
  |     }
  | 
  |     @Column(name="phone", length=50)
  |     public String getPhone() {
  |         return this.phone;
  |     }
  | 
  |     public void setPhone(String phone) {
  |         this.phone = phone;
  |     }
  | 
  |     @Column(name="latitude", precision=12, scale=0)
  |     public Float getLatitude() {
  |         return this.latitude;
  |     }
  | 
  |     public void setLatitude(Float latitude) {
  |         this.latitude = latitude;
  |     }
  | 
  |     @Column(name="longitude", precision=12, scale=0)
  |     public Float getLongitude() {
  |         return this.longitude;
  |     }
  | 
  |     public void setLongitude(Float longitude) {
  |         this.longitude = longitude;
  |     }
  | 
  |     @Column(name="firstname", length=256)
  |     public String getFirstname() {
  |         return this.firstname;
  |     }
  | 
  |     public void setFirstname(String firstname) {
  |         this.firstname = firstname;
  |     }
  | 
  |     @Column(name="lastname", length=256)
  |     public String getLastname() {
  |         return this.lastname;
  |     }
  | 
  |     public void setLastname(String lastname) {
  |         this.lastname = lastname;
  |     }
  | 
  |     @Column(name="organizationGuid", length=50)
  |     public String getOrganizationGuid() {
  |         return this.organizationGuid;
  |     }
  | 
  |     public void setOrganizationGuid(String organizationGuid) {
  |         this.organizationGuid = organizationGuid;
  |     }
  | 
  |     @Column(name="groupGuid", length=50)
  |     public String getGroupGuid() {
  |         return this.groupGuid;
  |     }
  | 
  |     public void setGroupGuid(String groupGuid) {
  |         this.groupGuid = groupGuid;
  |     }
  | 
  |     @Column(name="teamGuid", length=50)
  |     public String getTeamGuid() {
  |         return this.teamGuid;
  |     }
  | 
  |     public void setTeamGuid(String teamGuid) {
  |         this.teamGuid = teamGuid;
  |     }
  | 
  |     @Column(name="viewLevelGuid", length=50)
  |     public String getViewLevelGuid() {
  |         return this.viewLevelGuid;
  |     }
  | 
  |     public void setViewLevelGuid(String viewLevelGuid) {
  |         this.viewLevelGuid = viewLevelGuid;
  |     }
  | 
  |     @Column(name="viewAccessGuid", length=50)
  |     public String getViewAccessGuid() {
  |         return this.viewAccessGuid;
  |     }
  | 
  |     public void setViewAccessGuid(String viewAccessGuid) {
  |         this.viewAccessGuid = viewAccessGuid;
  |     }
  | 
  |     @Column(name="lastLogin", length=50)
  |     public String getLastLogin() {
  |         return this.lastLogin;
  |     }
  | 
  |     public void setLastLogin(String lastLogin) {
  |         this.lastLogin = lastLogin;
  |     }
  | 
  |     @Column(name="state")
  |     public Integer getState() {
  |         return this.state;
  |     }
  | 
  |     public void setState(Integer state) {
  |         this.state = state;
  |     }
  | 
  |     @Column(name="priority")
  |     public Integer getPriority() {
  |         return this.priority;
  |     }
  | 
  |     public void setPriority(Integer priority) {
  |         this.priority = priority;
  |     }
  | @OneToMany(fetch=FetchType.LAZY, mappedBy="subscriber")
  |     public Set<Subscribersession> getSubscribersessions() {
  |         return this.subscribersessions;
  |     }
  | 
  |     public void setSubscribersessions(Set<Subscribersession> subscribersessions) {
  |         this.subscribersessions = subscribersessions;
  |     }
  | @OneToMany(fetch=FetchType.LAZY, mappedBy="subscriber")
  |     public Set<Crunchroles> getCrunchroleses() {
  |         return this.crunchroleses;
  |     }
  | 
  |     public void setCrunchroleses(Set<Crunchroles> crunchroleses) {
  |         this.crunchroleses = crunchroleses;
  |     }
  | }
  | 

View the original post : http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4117758#4117758

Reply to the post : http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=4117758



More information about the jboss-user mailing list