Archive

Archive for the ‘XML’ Category

How to resolve circular dependency problems with a Document Literal Wrapped web service

15 June 2010 1 comment

A circular dependencies problem came up while I was trying to implement a Document Literal Wrapped style web service in JBoss. Having gone through several forums the main solution suggested was to use an RPC style web service since you cannot represent circular dependencies with a Document Literal. Alas, the jboss web services implementation does not support RPC ENCODED style web services since it considers it deprecated. An alternative solution was to use Axis but since the project was already using the JBoss implementation I didn’t want to add another one.

Before we delve into the solution lets see what a circular dependency is. In a few words, a circular dependency occurs when we have two objects, and each one refers to each other. For instance a payment and a receipt in the real world. You pay for something and you get a receipt. And the receipt you got refers to the payment you made. If we are to model this in Java we would have two classes, Payment and Receipt which could look like the following

class Payment
{
	private int paymentId;
	private Receipt receipt;
	
	public Payment() {
	}
	
	public void setPaymentId(int paymentId) {
		this.paymentId = paymentId;
	}

	public int getPaymentId() {
		return paymentId;
	}
	
	public void setReceipt(Receipt receipt) {
		this.receipt = receipt;
	}
	public Receipt getReceipt() {
		return receipt;
	}
}

class Receipt
{
	private int receiptId;
	private Payment payment;

	public Receipt() {
	}

	public void setReceiptId(int receiptId) {
		this.receiptId = receiptId;
	}

	public int getReceiptId() {
		return receiptId;
	}
	
	public void setPayment(Payment payment) {
		this.payment = payment;
	}

	public Payment getPayment() {
		return payment;
	}
}

This is circular dependency, Payment refers to Receipt and Receipt refers to Payment. Java can represent this easily in the object graph but in XML this is a bit tricky if you create the WSDL automatically.

Lets assume we have a web service that uses one of our classes.

@WebService(name="PaymentWS")
@SOAPBinding(style=SOAPBinding.Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle=SOAPBinding.ParameterStyle.WRAPPED) 
public class PaymentWS
{
	@WebMethod
	public void buy(Payment payment)
	{
	}
}

If we take the WSDL of our web service and generate the relevant classes and then we write a java client to connect to our web service we can write something like the following

public class Client {

    public static void main(String [] arguments)
    {
        PaymentWSService service = new PaymentWSService();
        PaymentWS ser = service.getPaymentWSPort();

        Payment payment = new Payment();
        payment.setPaymentId(10);

        Receipt receipt = new Receipt();
        receipt.setReceiptId(20);
        receipt.setPayment(payment);
        
        payment.setReceipt(receipt);
        
        ser.buy(payment);
    }
}

When we run this it throws a MarshalException

Caused by: javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.internal.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML:

This is because of the circular dependency of Payment and Receipt because the XML of the SOAP message tries to nest a chunk of XML Payment within the XML Receipt element, and a chunck or XML Receipt within the XML Payment and so on. We can get around this problem by using the @XmlIDREF and @XmlID annotations on the generated Java classes to instruct the runtime that instead of nesting the XML elements within each other simply to use a reference to them. Our generated java classes look like the following

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "payment", propOrder = {
    "paymentId",
    "receipt"
})
public class Payment {

    protected int paymentId;
    protected Receipt receipt;
    ...
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "receipt", propOrder = {
    "payment",
    "receiptId"
})
public class Receipt {

    protected Payment payment;
    protected int receiptId;
    ...
}

and after adding the annotations they become

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "payment", propOrder = {
    "paymentId",
    "receipt"
})
public class Payment {

    protected int paymentId;
    @XmlIDREF
    protected Receipt receipt;
    ...
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "receipt", propOrder = {
    "payment",
    "receiptId"
})
public class Receipt {

    protected Payment payment;
    @XmlID
    protected int receiptId;
    ...
}

Unfortunatelly this still does not work and it throws a WebServiceException which is caused by

Caused by: java.security.PrivilegedActionException: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
Property "receiptId" has an XmlID annotation but its type is not String.

This is because the runtime expects the type of the XmlId to be a string. Not sure why this is mandatory (I guess the designers where afraid that people might not implement the toString() method correctly). The workaround is to add an @XmlJavaTypeAdapter annotation which will adapt our non-String type to a String.

public class IntegerAdapter extends XmlAdapter<String, Integer> {
    
    public Integer unmarshal(String s) {
        return Integer.parseInt(s);
    }

    public String marshal(Integer number) {
        if (number == null) return "";
        
        return number.toString();
    }
}

Since the adapter works only with objects we will also need to modify the Receipt generated class to use an Integer intead of an int.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "receipt", propOrder = {
    "payment",
    "receiptId"
})
public class Receipt {

    protected Payment payment;
    @XmlID
    @XmlJavaTypeAdapter(IntegerAdapter.class)
    protected Integer receiptId;
    ...
}

That’s it. A bit tricky but at least it works.

Advertisements