Skip to main content

Problem with circular relationship when using JAXWS

77 replies [Last post]
korg
Offline
Joined: 2005-04-13
Points: 0

Hello guys,

I use JAXWS doc/lit and I found a problem when I try to return an object with a circular relationship. For example, let's assume the following class:

public class TestClass {
private Long id;
private TestClass myClass;

public TestClass (){}

public TestClass getMyClass() {
return myClass;
}
public void setMyClass(TestClass myClass) {
this.myClass = myClass;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

...and a web service with the following method:

@WebService
public class WSClassAImpl {

public TestClass testMethod(){
TestClass t1 = new TestClass ();
TestClass t2 = new TestClass ();

t1.setMyClass(t2);
t2.setMyClass(t1);

return t1;
}
}

There's a problem with the serialization of the object when it's time to return the object to the client side. I know that I could solve the problem by using RPC/encoded but it is not supported by JAXWS. Is it a bug and is there a plan to resolve this situation?? I mean, there MUST be a way to return such an object! Will there be a fix in future versions??

Thanks,
Philippe

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
zhucehao
Offline
Joined: 2012-06-18
Points: 0

It's bit hard to change the shape of XML like that. The unmarshaller needs to be aware of such possibilities, and that means JAXBContext needs to know upfront.

The feature is certainly nice to have. If only we can think about a cheap way to do it...

[img] http://www.apew.info/luoying3.jpg[/img]

[img] http://www.apew.info/luoying7.jpg[/img]

[img] http://www.apew.info/luoying2.jpg[/img]

kohsuke
Offline
Joined: 2003-06-09
Points: 0

jmcgarett ---

> A. Automagically handle cyclic dependency on both Java to XML and XML to Java or

None of us have come up with a solution to make this work yet. The problem is bit hard for JAXB as we normally have to generate the schema-valid XML. If you have a good suggestion (or better yet good patch!), please share it with us.

The onCycleDetected approach is not transparent to developers.

> Also will the 'gadams00' patches be applied to
> jaxb 2.1 only or also to the 2.0 tree also.

Right now the plan is to do this only on 2.1. I believe 2.1 is in a pretty good shape already, and it's a drop-in replacement to 2.0 --- all SPI/APIs exposed in the 2.0 are still there.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Thanks Greg, I applied your patch of cycle detection via property. I changed the patch a bit so that I don't have to pass in a boolean for each push and pop, and I also negated the flag value, as "identity" means ==, and "equality" means Object.equals() in Java.

I'll update the documentation shortly, so if you'll download the CI build of the JAXB RI 2.1, say, a few hours from now, you should see it in the documentation.

chillier
Offline
Joined: 2006-02-09
Points: 0

Hei -

I've been struggling with circular references in bidirectional relationships (via WebServices) for some time now.

This sounds like the cure, but I don't see how to access the "CI build of JAXB RI 2.1", with a sample in the documentation.

How do I practically go about getting this solution?

Best Regards,
Charles

gadams00
Offline
Joined: 2006-05-08
Points: 0

here's the patch:

Index: runtime/src/com/sun/xml/bind/v2/util/CollisionCheckStack.java
===================================================================
RCS file: /cvs/jaxb2-sources/jaxb-ri/runtime/src/com/sun/xml/bind/v2/util/CollisionCheckStack.java,v
retrieving revision 1.2
diff -r1.2 CollisionCheckStack.java
34c34
<
---
>
36c36
< initialHash = new int[17];
---
> initialHash = new int[17];
47c47
< public boolean push(E o) {
---
> public boolean push(E o, boolean useObjectIdentity) {
52,53c52,53
< int hash = hash(o);
< boolean r = findDuplicate(o, hash);
---
> int hash = hash(o, useObjectIdentity);
> boolean r = findDuplicate(o, hash, useObjectIdentity);
59c59,69
<
---
>
> /**
> * Pushes a new object to the stack.
> *
> * @return
> * true if this object has already been pushed
> */
> public boolean push(E o) {
> return push(o, false);
> }
>
82c92,95
< private int hash(Object o) {
---
> private int hash(Object o, boolean useObjectIdentity) {
> if (useObjectIdentity) {
> return Math.abs(o.hashCode() % initialHash.length);
> } else
89c102
< public E pop() {
---
> public E pop(boolean useObjectIdentity) {
97c110
< int hash = hash(o);
---
> int hash = hash(o, useObjectIdentity);
102a116,122
>
> /**
> * Pops an object from the stack
> */
> public E pop() {
> return pop(false);
> }
111c131
< private boolean findDuplicate(E o, int hash) {
---
> private boolean findDuplicate(E o, int hash, boolean useObjectIdentity) {
116c136,142
< if(existing==o) return true;
---
> if (useObjectIdentity) {
> if (o.equals(existing)) {
> return true;
> }
> } else {
> if(existing==o) return true;
> }
Index: runtime/src/com/sun/xml/bind/v2/runtime/XMLSerializer.java
===================================================================
RCS file: /cvs/jaxb2-sources/jaxb-ri/runtime/src/com/sun/xml/bind/v2/runtime/XMLSerializer.java,v
retrieving revision 1.42.2.4
diff -r1.42.2.4 XMLSerializer.java
468c468
< cycleDetectionStack.pop();
---
> cycleDetectionStack.pop(marshaller.isObjectIdentityCycleDetection());
484c484
< if(!cycleDetectionStack.push(obj))
---
> if(!cycleDetectionStack.push(obj, marshaller.isObjectIdentityCycleDetection()))
498c498
< cycleDetectionStack.pop();
---
> cycleDetectionStack.pop(marshaller.isObjectIdentityCycleDetection());
547c547
< cycleDetectionStack.pop();
---
> cycleDetectionStack.pop(marshaller.isObjectIdentityCycleDetection());
558c558
< cycleDetectionStack.pop();
---
> cycleDetectionStack.pop(marshaller.isObjectIdentityCycleDetection());
577c577
< cycleDetectionStack.pop();
---
> cycleDetectionStack.pop(marshaller.isObjectIdentityCycleDetection());
669c669
< cycleDetectionStack.pop();
---
> cycleDetectionStack.pop(marshaller.isObjectIdentityCycleDetection());
Index: runtime/src/com/sun/xml/bind/v2/runtime/MarshallerImpl.java
===================================================================
RCS file: /cvs/jaxb2-sources/jaxb-ri/runtime/src/com/sun/xml/bind/v2/runtime/MarshallerImpl.java,v
retrieving revision 1.39.4.2
diff -r1.39.4.2 MarshallerImpl.java
117a118,120
>
> /** Configured to use object identity cycle detection? */
> private boolean objectIdentityCycleDetection = false;
459a463,464
> if ( OBJECT_IDENTITY_CYCLE_DETECTION.equals(name))
> return objectIdentityCycleDetection;
506a512,516
> if (OBJECT_IDENTITY_CYCLE_DETECTION.equals(name)) {
> checkBoolean(name,value);
> objectIdentityCycleDetection = (Boolean)value;
> return;
> }
593a604,608
> protected static final String OBJECT_IDENTITY_CYCLE_DETECTION = "com.sun.xml.bind.objectIdentitityCycleDetection";
>
> public boolean isObjectIdentityCycleDetection() {
> return objectIdentityCycleDetection;
> }

Note: you might think it odd to pass the boolean into CollisionCheckStack.push, and so on, why not set the flag in the constructor? That was the road I went down initially, but CollisionCheckStack is contructed in XMLSerializer's constructor, and that is called from MarshallerImpl's constructor, before MarshallerImpl has had its properties set.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Thanks. It looks like the forum software ate some crucial whitespaces, and probably because of that patches are failing.

Can you send the patch (or the modified files) directly to me?

gadams00
Offline
Joined: 2006-05-08
Points: 0

I sent that patch via email yesterday.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Re: object identity vs equality.

Yes, that sounds like a realistic approach. Would you be interested in writing one more patch :-) ?

gadams00
Offline
Joined: 2006-05-08
Points: 0

Sure, I'll write another patch. It may be a few days though, I've got a lot to do currently.

jmcgarett
Offline
Joined: 2006-10-10
Points: 0

gadams00:

Firstly and foremostly thanks for posting a solution to this problem. Like you, I too am stuck with the same issue.

How will this approach work on the converse side where the XML needs to be converted back to a Java object?

kohsuke:
More specifically, I am wondering as to why the RI does not natively handle this situation via some type of referenceIDs. Seems a bit tentative to have the programmer have to write callbacks. Shouldn't the concept of "internalId" be formalized as a part of the java object serialization to xml and then thereafter de-serialization back from xml to java?

Again, thanks for your time and effort on this thread.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

I'm sorry, I don't understand what the problem is.

It's simply not possible to turn an cyclic object reference into an XML. So the best we can do is to provide a useful error message, which is what we do today.

Can somebody explain to me what behavior they are expecting?

tinashechipomho
Offline
Joined: 2005-01-24
Points: 0

I realized that its not that easy to turn any cyclic object reference into XML, but lets face real life/production situations people often have the following scenario.
[code]
class Parent{
private String name;
private List children;
.... getters and setters ....
}

class Child{
private String name;
private Parent parent;
... getters and setters ....
}
[/code]

or the following scenario number two.
[code]
class Parent{
private String name;
private Parent parent;
.... getters and setters ....
}
[/code]
and they want to get something like

ParentOne

Child-One


Child-Two


Child-Three


Child-Four



Child-One

Parent
.... other children if available ....

in case two expect something like

Parent

GrandParent

GrandGrandParent

this is a common scenario, usually the Parent and Child are entities which are managed via some web service.
While the schema creates a cyclic reference the actual object instances themselves are finite, and if they are finite sure enough an XML representation is possible.

I hope this helps. Should I raise a bug or put it as an enhancement.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

OK, I think what people need is an extension to the JAXB guide (https://jaxb.dev.java.net/guide/) that talks about how to map those cyclic references into XML.

I don't think it's a "show-stopper bug" of JAXB, because we do offer annotations and other means to let you do this, like your very example. What we need to do is to explain how those means can be used to achieve the goal.

(Off to updating the document...)

kohsuke
Offline
Joined: 2003-06-09
Points: 0
spiritofnight
Offline
Joined: 2008-10-22
Points: 0

Looking for this page:
https://jaxb.dev.java.net/guide/Mapping_cyclic_references_to_XML.html
the only similar I found was this other:
http://jaxb.java.net/guide/Mapping_cyclic_references_to_XML.html

This one solved my doubts, so I just write to recommend user looking for
Mapping cyclic references to XML
to go to the page above, without loosing time looking for a non-existent web (seems moved).

As told, I couldn't access the previous one, so I cannot say whether the content is similar or not :S

Cheers

spiritofnight
Offline
Joined: 2008-10-22
Points: 0

Looking for this page:
https://jaxb.dev.java.net/guide/Mapping_cyclic_references_to_XML.html
the only similar I found was this other:
http://jaxb.java.net/guide/Mapping_cyclic_references_to_XML.html

This one solved my doubts, so I just write to recommend user looking for
Mapping cyclic references to XML
to go to the page above, without loosing time looking for a non-existent web (seems moved).

As told, I couldn't access the previous one, so I cannot say whether the content is similar or not :S

Cheers

gadams00
Offline
Joined: 2006-05-08
Points: 0

I've recently run into this using JAXB 2.0.1. I'm getting Stack Overflow with cycles that are difficult for me to anticipate (i.e. cycles like A->B->C->A).

I walked through the source a little bit and I have a few questions: Is there a reason you used identity for cycle detection (System.identityHashCode(o) instead of o.hashCode() in com.sun.xml.bind.v2.util.CollisionCheckStack.hash and
if(existing==o) instead of if(existing.equals(o)) in com.sun.xml.bind.v2.util.CollisionCheckStack.findDuplicate)? I'd like to be able to implement equals and hashCode in my beans and have that used for cycle detection. I have a case where this code is comparing a proxy instance to a normal instance of the same bean with the same business identity, and the use of system identity is preventing the detection of a cycle in this case where I need it detected.

Also, when a cycle is detected I would like to omit the duplicate object from serialization, just skip it, since it's already been serialized in the current operation and no data will be lost if it's not serialized again. I think this is what the majority of JAXB users are looking for the serializer to do. Perhaps it would be possible to use a vendor extension to enable this behavior?

kohsuke
Offline
Joined: 2003-06-09
Points: 0

The reason identity hash code and == are used is because if I use Object.equals and hashCode(), then I could flag a false positive.

I'd like to better understand your problem, though. Is it that you are creating a new proxy instance on the get method and every invocation returns a new instance? But then given the way the JAXB RI works with interfaces, I don't understand how you manage to have JAXB marshal your proxies.

As for simply skipping the duplicate object, I don't think such behavior is that useful, because the receiving side won't be able to recreate the same graph.

That said, today, the cycle error is a fatal error, so one could argue any recovery behavior is better than a fatal error.

gadams00
Offline
Joined: 2006-05-08
Points: 0

Thanks for responding, kohsuke.

I encounter the lack of identification of cycles when using Hibernate, which causes CGLIB proxies to be returned in some cases, and the actual class to be returned in others. In the particular case I'm testing the root of the object graph is an actual object, while the backwards reference is to the proxy to the object. There is no interface involved, CGLIB can proxy a class that doesn't implement an interface. In this case, identity hash code and == don't identify them as being the same instance (which I guess you could argue they're not), but my class implements equals and hashCode, and I'd like JAXB to detect this as a cycle. Don't the default implementations of equals and hashCode do what you're doing anyways? If the developer of the class being marshalled overrode equals and hashCode, is it not valid to use those?

As for skipping the duplicate objects, I think you're right, I may have spoken too soon. Perhaps a better solution would be to use the XmlID and XmlIDREF behavior in such cases, if it's possible (i.e. if the original class being duplicated has an @XmlID annotation on some property, then turn this containment into an id reference). Would that be possible and would it work?

kohsuke
Offline
Joined: 2003-06-09
Points: 0

What's puzzling to me is that if the # of proxies involved here is finite, then the loop will be eventually found. If JAXB is going 'spiral', then it must mean that new proxies are created every time it accesses the getter, which seems to be somewhat unusual design of things.

Another reason I hesitate to change the current cycle detection behavior is that it could cause regressions, and we generally try very hard not to do that. And when that regression is flagging an error on a false positive, people would get upset. In contrast, the only thing we get with equals/hashCode based cycle detection is a difference in error message (SOE or better error message that points to a cycle.) So in comparsing those pros and cons, I'm still unable to convince myself.

Yes, @XmlID and IDREF are more desirable and robust solutions for problems like this. See https://jaxb.dev.java.net/guide/Mapping_cyclic_references_to_XML.html

gadams00
Offline
Joined: 2006-05-08
Points: 0

> What's puzzling to me is that if the # of proxies
> involved here is finite, then the loop will be
> eventually found. If JAXB is going 'spiral', then it
> must mean that new proxies are created every time it
> accesses the getter, which seems to be somewhat
> unusual design of things.

Well, that's definitely what's happening with Hibernate. I get SOE every time.

>
> Yes, @XmlID and IDREF are more desirable and robust
> solutions for problems like this. See
> https://jaxb.dev.java.net/guide/Mapping_cyclic_referen
> ces_to_XML.html

I agree that XmlID and XmlIDREF are a good fit for this problem, but my only problem with them is that they are an always-or-never kind of approach. If I annotate my classes with @XmlID and @XmlIDREF I always get serialization by reference. If I don't make sure the instance being represented by an XmlIDREF is not included by containment somewhere, it isn't serialized at all. I'm using my JAXB-annotated classes as JAX-WS webservice return types and arguments, and I can't tell ahead of time which of my JAXB-annotated classes will be used as root types and which will not, so I can't make any intelligent choice about what to represent as XmlIDREF's and what not to. I also have cycles that legitimately show up in my model and I really have no way to handle the situation, because in one situation I want one end of the cycle to get serialized and in another I want the opposite. This is why I'm struggling trying to find a way to handle cycles at runtime.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Yeah, it's conceivable that we have something inbetween @XmlID and ordinary in-place reference, where the decision is made at the runtime by whether it was already serialized once or not.

While this is very uncommon in traditional XML vocabulary design, this does seem like an useful feature when you are dumping objects to memory.

(And Ant has a similar XML design like )

The tricky part is such constraint is usually hard to express in XML Schema, and there's virtually no hope of having another tool consume such a schema in a meaningful way.

gadams00
Offline
Joined: 2006-05-08
Points: 0

Okay, I concede. Instead of handling this in the JAXB runtime, how about a hook for customization, so that the JAXB user can decide how to handle it. I'm thinking of a callback-style customization, like the before/after Marshal/Unmarshal callbacks. Perhaps we could pass the object instance that was detected as a cycle, and allow the user to modify what's being serialized (i.e. erase all fields except for an identifier).

kohsuke
Offline
Joined: 2003-06-09
Points: 0

It's bit hard to change the shape of XML like that. The unmarshaller needs to be aware of such possibilities, and that means JAXBContext needs to know upfront.

The feature is certainly nice to have. If only we can think about a cheap way to do it...

gadams00
Offline
Joined: 2006-05-08
Points: 0

I'm not sure I follow you. I've concocted a simple example that produces the error message with the JAXB RI, then I grabbed the source for JAXB and modified the runtime a bit to try out my suggestion. It seems to work in my simple example.

Here's my JAXB-annotated POJO:
[code]
package model;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Person {

private Long internalId;

private String name;

private Person parent;

public Long getInternalId() {
return internalId;
}

public void setInternalId(Long internalId) {
this.internalId = internalId;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Person getParent() {
return parent;
}

public void setParent(Person parent) {
this.parent = parent;
}

public Person onCycleDetected(Person p) {
Person retPerson = new Person();
retPerson.setInternalId(p.getInternalId());
return retPerson;
}
}
[/code]
and here's my main method:
[code]
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance("model");
Marshaller m = jc.createMarshaller();

Person p = new Person();
p.setInternalId(1L);
p.setName("Person1");
p.setParent(p);
m.marshal(p, System.out);
}
[/code]
here's the patch:
[code]
Index: runtime/src/com/sun/xml/bind/v2/runtime/XMLSerializer.java
===================================================================
RCS file: /cvs/jaxb2-sources/jaxb-ri/runtime/src/com/sun/xml/bind/v2/runtime/XMLSerializer.java,v
retrieving revision 1.42
diff -r1.42 XMLSerializer.java
470c470
< private void pushObject(Object obj, String fieldName) throws SAXException {
---
> private Object pushObject(Object obj, String fieldName) throws SAXException {
473,487c473,494
< StringBuilder sb = new StringBuilder();
< sb.append(obj);
< int i=cycleDetectionStack.size()-1;
< Object x;
< do {
< sb.append(" -> ");
< x = cycleDetectionStack.get(--i);
< sb.append(x);
< } while(obj!=x);
<
< reportError(new ValidationEventImpl(
< ValidationEvent.ERROR,
< Messages.CYCLE_IN_MARSHALLER.format(sb),
< getCurrentLocation(fieldName),
< null));
---
> try {
> Method m = obj.getClass().getMethod("onCycleDetected", new Class[] {obj.getClass()});
> obj = m.invoke(obj, new Object[] {obj});
> } catch (Exception e) {
> e.printStackTrace();
> }
>
> // StringBuilder sb = new StringBuilder();
> // sb.append(obj);
> // int i=cycleDetectionStack.size()-1;
> // Object x;
> // do {
> // sb.append(" -> ");
> // x = cycleDetectionStack.get(--i);
> // sb.append(x);
> // } while(obj!=x);
> //
> // reportError(new ValidationEventImpl(
> // ValidationEvent.ERROR,
> // Messages.CYCLE_IN_MARSHALLER.format(sb),
> // getCurrentLocation(fieldName),
> // null));
488a496
> return obj;
521c529
< pushObject(child,fieldName);
---
> child = pushObject(child,fieldName);
574c582
< pushObject(child,fieldName);
---
> child = pushObject(child,fieldName);
[/code]

the xml output looks like this:
[code]

1
Person1

1
[/code]
This is actually somewhat usable to me. Despite the fact that the original object hierarchy is changed in the marshalling/demarshalling process in this case, the original information is at least derivable, instead of being completely unable to serialize such a model at all.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Ah, nice! My hats off to you.

So you let the class nominate its substitute. With a little more change, you can even nominate a substitute from another class, which should be even more useful.

Could you sign https://glassfish.dev.java.net/public/GovernancePolicy.html#SCA_Policy and send/FAX it to us? I'd love to incorporate your patch, and to do that I need you to sign this SCA for us.

gadams00
Offline
Joined: 2006-05-08
Points: 0

I emailed a signed, scanned copy to sun_ca@sun.com and CC'd you.

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Thanks. I'm in the process of incorporating your change.

gadams00
Offline
Joined: 2006-05-08
Points: 0

Okay, so that will make it possible for the cycles to be serialized in a semi-sensible format. I still will have problems due to my use of Hibernate with the inability to use Object.equals() and Object.hashCode() instead of system identity when detecting cycles. I was thinking that perhaps we could add a custom, JAXB2-RI only property to MarshallerImpl, something like "com.sun.xml.bind.useObjectIdentityCycleDetection" that we could check in XMLSerializer and use in CollisionCheckStack.hashCode and CollisionCheckStack.findDuplicate. Does that sound reasonable?

kohsuke
Offline
Joined: 2003-06-09
Points: 0

I completed the integration of your cycle detection/recovery patch. I slightly modified it and added the 'cycle-recovery' example to illustrate the use.

lincolnbaxter
Offline
Joined: 2008-03-26
Points: 0

There is another potential solution for this issue:

Assuming:

Parent.class contains a list of Child.class with backreferences to Parent

You can use an XmlAdapter (using @XmlJavaTypeAdapter(ParentAdapter.class)
Child.getParent())

This allows you to modify the value of Child.getParent() that is marshalled into XML containing the parent Id. On return trip/unmarshalling, the parent field will be populated with a new object containing only "id" -- it prevents nullpointers, but might be somewhat misleading... null may be preferred, in which case, use the @XmlTransient solution.

[code]
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class ParentAdapter extends XmlAdapter
{
@Override
public Long marshal(Parent parent) throws Exception
{
return parent.getId();
}

@Override
public Parent unmarshal(Long id) throws Exception
{
return new Parent().setId(id);
}

}
[/code]
Message was edited by: lincolnbaxter

Message was edited by: lincolnbaxter

rahul_juneja
Offline
Joined: 2003-07-17
Points: 0

I did not understand it.
Appreciate a little bit more explanation with an example

Thanks,
Rahul

Glen Mazza

https://jaxb.dev.java.net/guide/Mapping_cyclic_references_to_XML.html ?

metro-3 wrote:
>
> I did not understand it.
> Appreciate a little bit more explanation with an example
>
> Thanks,
> Rahul
>

--
View this message in context: http://www.nabble.com/Re%3A-Problem-with-circular-relationship-when-usin...
Sent from the Metro - Users mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@metro.dev.java.net
For additional commands, e-mail: users-help@metro.dev.java.net

rcn
Offline
Joined: 2009-04-08
Points: 0

Maybe you found out already, but here I go anyway: one may use the annotations @XmlRootElement and @XmlTransient.
So, if we have a scenario like: users – roles, the multiplicity being “many-to-one”, then we annotate the entity of Roles with @XmlRootElement, and, in the Users entity, annotate the roles attribute with @XmlTransient.
It worked for me, using JAXB 2.1

rahul_juneja
Offline
Joined: 2003-07-17
Points: 0

Folks i am having the same problem. and i did put @XmlTransient on the getter of my primary class. Also i am using EJB3 based services with all anotations.

putting @XmlTransient didn't help.

Any more clues. or anything specific which i am missing. Also i am using Jaxb-api-2.1.jar

Any pointers are highly appreciated.

Thanks,
Rahul

fernando_lp
Offline
Joined: 2006-08-07
Points: 0

Hy everyone,
I had this same problem with circular relationship on JAXB, and I post a message on mail list of jax-ws / jaxb.
So, Dima Gutzeit response to me suggesting to put the @XmlTransient above the getter of property with circular problem, and works fine to me !

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Good. This thread is truly becoming the longest thread in this forum. That probably means this is one of the most common topic that many users have an issue with.

I better expand https://jaxb.dev.java.net/guide/Mapping_cyclic_references_to_XML.html to start with, but I just wonder if there's anything we can do better here...

rahul_juneja
Offline
Joined: 2003-07-17
Points: 0

Folks i am having the same problem. and i did put @XmlTransient on the getter of my primary class. Also i am using EJB3 based services with all anotations.

putting @XmlTransient didn't help.

Any more clues. or anything specific which i am missing. Also i am using Jaxb-api-2.1.jar

Any pointers are highly appreciated.

Thanks,
Rahul

jmcgarett
Offline
Joined: 2006-10-10
Points: 0

Yes Kohsuke, I am exercising JAXB via JAX-WS. Is there any work around to setting Marshaller properties if JAX-WS underneath covers is using JAXB? Also, is there any upcoming JAX-WS enhancement to allow that? Thanks much.

jmcgarett
Offline
Joined: 2006-10-10
Points: 0

Charles:

Here is a link:

https://jaxb.dev.java.net/files/documents/51/42455/jaxb-ri.zip

Hope this helps some.

JM

chillier
Offline
Joined: 2006-02-09
Points: 0

Yes, Thanks for the link, got the download and example.
Best Regards,
Charles

chillier
Offline
Joined: 2006-02-09
Points: 0

Hei -

I have tried the onCycleDetected with the new CI build JAXB 2.1 jar files, but with same result. (circular references)

I see people using "JAXB Annotations" is this necessary?( @XmlRootElement, etc).

My simple circ. ref:
------------------------------------
package warehouse3;

import com.sun.xml.bind.CycleRecoverable;

import java.io.Serializable;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;

@Entity
@NamedQuery(name = "Item.findAll", query = "select o from Item o")
public class Item implements Serializable, CycleRecoverable {
@Column(name="ITEM_CLOB")
private char[] itemClob;
@Column(name="ITEM_DESC", nullable = false)
private String itemDesc;
@Id
@Column(name="ITEM_ID", nullable = false)
private Long itemId;
@OneToMany(mappedBy = "item")
private List stockList;

public Item() {
}

public char[] getItemClob() {
return itemClob;
}

public void setItemClob(char[] itemClob) {
this.itemClob = itemClob;
}

public String getItemDesc() {
return itemDesc;
}

public void setItemDesc(String itemDesc) {
this.itemDesc = itemDesc;
}

public Long getItemId() {
return itemId;
}

public void setItemId(Long itemId) {
this.itemId = itemId;
}

public List getStockList() {
return stockList;
}

public void setStockList(List stockList) {
this.stockList = stockList;
}

public Stock addStock(Stock stock) {
getStockList().add(stock);
stock.setItem(this);
return stock;
}

public Stock removeStock(Stock stock) {
getStockList().remove(stock);
stock.setItem(null);
return stock;
}

public String toString() {
return "\n\t[ " + itemId + " " + itemDesc +" ]";
}

// this method is called by JAXB when a cycle is detected
public Item onCycleDetected(Context context) {
// when a cycle is detected, let's just write out an ID
Item replacement = new Item();
replacement.itemId = this.itemId;
return replacement;
}
}
--------------------
package warehouse3;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQuery;
import com.sun.xml.bind.CycleRecoverable;

@Entity
@NamedQuery(name = "Stock.findAll", query = "select o from Stock o")
public class Stock implements Serializable, CycleRecoverable {
@Column(name="STOCK_COUNT")
private Long stockCount;
@Column(name="STOCK_DESC", nullable = false)
private String stockDesc;
@Id
@Column(name="STOCK_ID", nullable = false)
private Long stockId;
@ManyToOne
@JoinColumn(name = "ITEM_REF", referencedColumnName = "ITEM_ID")
private Item item;
@ManyToOne
@JoinColumn(name = "WAREHOUSE_REF", referencedColumnName = "WAREHOUSE_ID")
private Warehouse warehouse;

public Stock() {
}

public Long getStockCount() {
return stockCount;
}

public void setStockCount(Long stockCount) {
this.stockCount = stockCount;
}

public String getStockDesc() {
return stockDesc;
}

public void setStockDesc(String stockDesc) {
this.stockDesc = stockDesc;
}

public Long getStockId() {
return stockId;
}

public void setStockId(Long stockId) {
this.stockId = stockId;
}

public Item getItem() {
return item;
}

public void setItem(Item item) {
this.item = item;
}

public Warehouse getWarehouse() {
return warehouse;
}

public void setWarehouse(Warehouse warehouse) {
this.warehouse = warehouse;
}

public String toString() {
return "\n\t[ " + stockId + " " + stockDesc +" ]";
}

// this method is called by JAXB when a cycle is detected
public Stock onCycleDetected(Context context) {
// when a cycle is detected, let's just write out an ID
Stock replacement = new Stock();
replacement.stockId = this.stockId;
return replacement;
}
}

findAllItem:
SEVERE: circular reference detected while serializing: warehouse3.Stock circular reference detected while serializing: warehouse3.Stock

chillier
Offline
Joined: 2006-02-09
Points: 0

OK...

Now I see that JAXB comes imbedded i JAX-WS, and I am wondering how to know if I am using the new CI Build JAXB 2.1, even though I am compiling and deploying successfully. Any tips ?

Think I'm missing some major info here...

Best Regards,
Charles

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Try JAXBContext.newInstance().toString().

This is not completely reliable, as JAX-WS RI may live in a different environment with different classloader etc. But it would be a good start.

I'll improve the JAX-WS RI 2.1 so that it can report back what version of the JAXB RI it's using behind the scene.

Finally, have you looked at https://jaxb.dev.java.net/guide/Mapping_cyclic_references_to_XML.html
?

chillier
Offline
Joined: 2006-02-09
Points: 0

Hei kohsuke _

Yeah, tried JAXBContext.newInstance().toString()., and at least my webservice reported the correct version of JAXB, but still have circular references, and I am still not entirely sure what JAX-WS is actually using...

You don't have a secret version of JAX-WS RI 2.1 somewhere?

Finally, looked at https://jaxb.dev.java.net/guide/Mapping_cyclic_references_to_XML.html
But was
1) unable to see how to implement bidirectional relationships since the OneToMany is needed one way(ManyToOne), but transient the other (as parent), and
2) Even the simple @OneToMany/@ManyToOne relationships seems to ignore my every JAXB instruction, giving my unholy circular references again.

thnx
Charles

kohsuke
Offline
Joined: 2003-06-09
Points: 0

Sorry, what do you mean by "your webservice reported the correct version of JAXB"?

> 1) unable to see how to implement bidirectional relationships since the OneToMany is needed one way(ManyToOne), but transient the other (as parent), and
> 2) Even the simple @OneToMany/@ManyToOne relationships seems to ignore my every JAXB instruction, giving my unholy circular references again.

I think I really need more details about what you tried and what you got, etc.

chillier
Offline
Joined: 2006-02-09
Points: 0

Sorry kohsuke -
I'm Using Jdeveloper/OC4J right now, though the problem is identicial with Netbeans/Glassfish. I deployed your new JAXB in a "shared library" (the classes are found in JDev, but OC4J required a "shared library".

Then I added a method to my SessionBean(Facade):
public String getJAXBver() {
try {
return JAXBContext.newInstance().toString();
} catch (JAXBException e) {
return "JAXBException......";
}
}
Adding the @WebService annotation exposes this method as a webservice, and can be tested in OC4J (quite similar to Glassfish webservices testing). The call returned:
code-source:/C:/Oracle/oc4j/j2ee/home/shared-lib/JAXB/2.1/jaxb-impl.jar!/com/sun/xml/bind/v2/runtime/JAXBContextImpl.class Build-Id: 2.1
Classes known to this context:
[B
boolean
byte
char
com.sun.xml.bind.api.CompositeStructure
double
float
int
java.awt.Image
java.io.File
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.Class
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Object
java.lang.Short
java.lang.String
java.lang.Void
java.math.BigDecimal
java.math.BigInteger
java.net.URI
java.net.URL
java.util.Calendar
java.util.Date
java.util.GregorianCalendar
java.util.UUID
javax.activation.DataHandler
javax.xml.bind.JAXBElement
javax.xml.datatype.Duration
javax.xml.datatype.XMLGregorianCalendar
javax.xml.namespace.QName
javax.xml.transform.Source
long
short
void

Which certainly appear to have found my JAXB shared library.

My simplest Item/Stock code:
####################################
package warehouse3;

import com.sun.xml.bind.CycleRecoverable;

import java.io.Serializable;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;

import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@Entity
@NamedQuery(name = "Item.findAll", query = "select o from Item o")
@XmlRootElement
public class Item implements Serializable, CycleRecoverable {
@Column(name="ITEM_CLOB")
private char[] itemClob;
@Column(name="ITEM_DESC", nullable = false)
private String itemDesc;
@Id
@Column(name="ITEM_ID", nullable = false)
private Long itemId;
@OneToMany(mappedBy = "item")
@XmlElement
private List stockList;

public Item() {
}

public char[] getItemClob() {
return itemClob;
}

public void setItemClob(char[] itemClob) {
this.itemClob = itemClob;
}

public String getItemDesc() {
return itemDesc;
}

public void setItemDesc(String itemDesc) {
this.itemDesc = itemDesc;
}

public Long getItemId() {
return itemId;
}

public void setItemId(Long itemId) {
this.itemId = itemId;
}

public List getStockList() {
return stockList;
}

public void setStockList(List stockList) {
this.stockList = stockList;
}

public Stock addStock(Stock stock) {
getStockList().add(stock);
stock.setItem(this);
return stock;
}

public Stock removeStock(Stock stock) {
getStockList().remove(stock);
stock.setItem(null);
return stock;
}

public String toString() {
return "\n\t[ " + itemId + " " + itemDesc +" ]";
}

// this method is called by JAXB when a cycle is detected
public Item onCycleDetected(Context context) {
// when a cycle is detected, let's just write out an ID
Item replacement = new Item();
replacement.itemId = this.itemId;
return replacement;
}

}
#########################################3
package warehouse3;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQuery;
import com.sun.xml.bind.CycleRecoverable;

import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

@Entity
@NamedQuery(name = "Stock.findAll", query = "select o from Stock o")
public class Stock implements Serializable, CycleRecoverable {
@Column(name="STOCK_COUNT")
private Long stockCount;
@Column(name="STOCK_DESC", nullable = false)
private String stockDesc;
@Id
@Column(name="STOCK_ID", nullable = false)
private Long stockId;
@XmlTransient
@ManyToOne
@JoinColumn(name = "ITEM_REF", referencedColumnName = "ITEM_ID")
private Item item;
@XmlTransient
@ManyToOne
@JoinColumn(name = "WAREHOUSE_REF", referencedColumnName = "WAREHOUSE_ID")
private Warehouse warehouse;

public Stock() {
}

public Long getStockCount() {
return stockCount;
}

public void setStockCount(Long stockCount) {
this.stockCount = stockCount;
}

public String getStockDesc() {
return stockDesc;
}

public void setStockDesc(String stockDesc) {
this.stockDesc = stockDesc;
}

public Long getStockId() {
return stockId;
}

public void setStockId(Long stockId) {
this.stockId = stockId;
}

public Item getItem() {
return item;
}

public void setItem(Item item) {
this.item = item;
}

public Warehouse getWarehouse() {
return warehouse;
}

public void setWarehouse(Warehouse warehouse) {
this.warehouse = warehouse;
}

public String toString() {
return "\n\t[ " + stockId + " " + stockDesc +" ]";
}

// this method is called by JAXB when a cycle is detected
public Stock onCycleDetected(Context context) {
// when a cycle is detected, let's just write out an ID
Stock replacement = new Stock();
replacement.stockId = this.stockId;
return replacement;
}

public void afterUnmarshal(Unmarshaller u, Object parent) {
this.item = (Item)parent;
}
}
###############################

I have tried several varieties of this code with transient instead of @XmlTransient etc, with no help.

Best Regards,
Charles

kohsuke
Offline
Joined: 2003-06-09
Points: 0

I'm afraid I need to ask you to run this under the debugger to see what's going on.

You can set a breakpoint on com/sun/xml/bind/v2/runtime/XMLSerializer.java line 488, where if does "if(obj instanceof CycleRecoverable)".

The JAX-WS RI should come with the JAXB RI soure zip file. If not, you can download the JAXB RI separetely and it will come with the source zip bundle.

chillier
Offline
Joined: 2006-02-09
Points: 0

Hei Kohsuke -

I am running this application on the GlassFish server, and don't know how to test a breakpoint in the server, outside NetBeans. I'll start looking for what I can do, but maybe you could be more explicit about how I can get this debugger information (I need to be able to debug this type of problem, anyway).

I don't think I can deploy jaxb-api.jar with a NetBeans breakpoint to GlassFish, can I? (GlassFish in debug mode recognizes NetBeans breakpoint?).

I'll investigate...

BR/Charles

chillier
Offline
Joined: 2006-02-09
Points: 0

OK -

After visiting this blog:
http://weblogs.java.net/blog/cayhorstmann/archive/2006/06/programmer_pro...

It looks exciting - have found, modified source with breakpoint, trying "Debug Project", which is trying to start glassfish in debug....... Makes some NetBeans believers here... (we are also using JDeveloper)

BR/Charles