[wsdlc] [ERROR] A class/interface with the same name “com.mydomain.layer.inbound.ws.ValidateResponse” is already in use. Use a class customizat ion to resolve this conflict.
I got the error [wsdlc] [ERROR] A class/interface with the same name "com.mydomain.layer.inbound.ws.ValidateResponse" is already in use. Use a class customization to resolve this conflict.
a few days ago when I tried to generate the Java code from a WSDL file, using Oracle’s wsdlc
Ant task. The problem is caused by having two elements with the same name, but with a different namespace, in the WSDL file. For example the following bit of code will result in the error above:
<s:element name="ValidateResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="ValidateResult" type="tns:ValidateResponse" /> </s:sequence> </s:complexType> </s:element> <s:complexType name="ValidateResponse"> <s:complexContent mixed="false"> <s:extension base="tns:Response"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="Status" type="tns:ResponseStatus" /> <s:element minOccurs="0" maxOccurs="1" name="SubscriptionExpiry" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Allowed" type="tns:ArrayOfGuid" /> </s:sequence> </s:extension> </s:complexContent> </s:complexType>
The solution to this is to use a JAXB Customisation Binding, as explained here. By using a JAXB Customisation Binding you can essentially bind a WSDL element to another type and therefore resolve the conflict, since in the generated Java code you end up with two different artifacts.
There are two ways do it. The first one is to use an external JAXB Customisation Binding file. Unfortunately this solution did not work for me. Not sure what the problem was. The second solution is to use inline JAXB Customisation Binding code. Fortunately this solution did work for me. By inlining the JAXB binding you end up with the following bit of code
<s:element name="ValidateResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="ValidateResult" type="tns:ValidateResponse" /> </s:sequence> </s:complexType> </s:element> <s:complexType name="ValidateResponse"> <s:annotation> <s:appinfo> <jaxb:class name="ValidateResponseComplexType"/> </s:appinfo> </s:annotation> <s:complexContent mixed="false"> <s:extension base="tns:Response"> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="Status" type="tns:ResponseStatus" /> <s:element minOccurs="0" maxOccurs="1" name="SubscriptionExpiry" type="s:string" /> <s:element minOccurs="0" maxOccurs="1" name="Allowed" type="tns:ArrayOfGuid" /> </s:sequence> </s:extension> </s:complexContent> </s:complexType>
When the wsdlc
runs you will end up with the different classes for the ValidateResponse element: ValidateResponse
and ValidateResponseComplexType
. Of course you will also need to define the jaxb namespace: xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
in the WSDL file.
How to create and secure a POJO web service in JBoss (in seven easy steps)
1) Create the web service (we will create a Document Literal Wrapped web service)
package sample.web.service; import javax.jws.WebMethod; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; @WebService(name="Hello") @SOAPBinding(style=SOAPBinding.Style.DOCUMENT, use=SOAPBinding.Use.LITERAL, parameterStyle=SOAPBinding.ParameterStyle.WRAPPED) public class Hello { @WebMethod public String sayHello(String name) { return "Hello " + name + " from the web service."; } }
2) Add the service and a security constraint in the web.xml
<servlet> <display-name>Hello Web Service</display-name> <servlet-name>HelloService</servlet-name> <servlet-class>sample.web.service.Hello</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>HelloService</servlet-name> <url-pattern>/HelloService</url-pattern> </servlet-mapping> <security-constraint> <display-name>Restrict WS Access</display-name> <web-resource-collection> <web-resource-name>WS</web-resource-name> <url-pattern>/HelloService</url-pattern> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> </security-constraint> <security-role> <role-name>admin</role-name> </security-role> <login-config> <auth-method>BASIC</auth-method> <realm-name>JBossWS</realm-name> </login-config>
3) Edit the jboss-web.xml
file and add the jaas security domain
<security-domain>java:/jaas/JBossWS</security-domain>
The JBossWS
is the application policy’s name in the login-config.xml
. It is also defined as a realm in the login-config
element of the web.xml
.
4) Edit the jbossws-users.properties
and jbossws-roles.properties
files that are mentioned under the JBossWS
application policy XML element in the login-config.xml
file.
In jbossws-users.properties
add a username/password pair.
panos=panos
In jbossws-roles.properties
add the role of the user (the one defined in the web.xml
) and the corresponding user.
panos=admin
5) Deploy the web service on JBoss.
6) Create the client’s stubs from the web service’s WSDL.
7) Write the Java client that authenticates the user.
import hellostubs.*; import java.util.Map; import javax.xml.ws.BindingProvider; /** * * @author pkonstantinidis */ public class Client { public static void main(String [] arguments) throws Exception { HelloService service = new HelloService(); Hello hello = service.getHelloPort(); BindingProvider provider = (BindingProvider) hello; Map<String, Object> authentication = provider.getRequestContext(); authentication.put(BindingProvider.USERNAME_PROPERTY, "panos"); authentication.put(BindingProvider.PASSWORD_PROPERTY, "panos"); System.out.println(hello.sayHello("Alexander")); } }
How to resolve circular dependency problems with a Document Literal Wrapped web service
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.