Problem:
When JAXB unmarshalls the xml into the object tree, the attributes of objects from a seperate jar (and schema) are not set and remain null.
See the code below. On unmarshalling, the attributes of the MeldingType class remain NULL, even though the SOAP message has filled the 'code' and 'melding' child-elemens in the 'meldingen' elements.
If I create a 'local' version of MeldingType in the .war and also place the .xsd definition of MeldingType in the out.xsd, everything works perfectly. :(
How can I work with a seperate jar, so that I can share common JAXB objects from common .xsd's?
Situation
I have a war and a jar file.
The .war contains a class 'RequestType', package-info, out.xsd and out.wsdl.
The .jar contains a class 'MeldingType', package-info, and schema.xsd
WAR FILE
Package info:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://schemas.xxx.nl/schema/out/v0010", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package nl.xxx.out.web.model;
RequestType
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "XbOutRequestType", propOrder = {
"kenmerk",
"meldingen"
})
public class XbOutRequestType {
#XmlElement(required = true)
protected String kenmerk;
#XmlElement(required = true)
protected List<MeldingType> meldingen = new ArrayList<MeldingType>();
public String getKenmerk() {
return this.kenmerk;
}
public List<MeldingType> getMeldingen() {
return this.meldingen;
}
}
out.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ref="http://schemas.xxx.nl/schema/v0010"
targetNamespace="http://schemas.xxx.nl/out/v0010"
xmlns="http://schemas.xxx.nl/out/v0010"
elementFormDefault="qualified">
<xsd:element name="request" type="RequestType"/>
<xsd:element name="respomse" type="ResponseType"/>
<xsd:complexType name="RequestType">
<xsd:sequence>
<xsd:element name="kenmerk" type="ref:kenmerkType" minOccurs="1"/>
<xsd:element name="meldingen" type="ref:MeldingType" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="XbOutResponseType"/>
</xsd:schema>
out.wsdl
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://schemas.xxx.nl/wsdl/out/v0010"
xmlns:typ="http://schemas.xxx.nl/schema/out/v0010"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
name="XbRender" targetNamespace="http://schemas.xxx.nl/wsdl/out/v0010">
<wsdl:types>
<xsd:schema targetNamespace="http://schemas.xxx.nl/wsdl/out/v0010" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:import namespace="http://schemas.xxx.nl/schema/out/v0010" schemaLocation="out.xsd" />
</xsd:schema>
</wsdl:types>
<wsdl:message name="request">
<wsdl:part name="body" element="typ:request" />
</wsdl:message>
<wsdl:message name="response">
<wsdl:part name="body" element="typ:response" />
</wsdl:message>
<wsdl:portType name="portType">
<wsdl:operation name="execute">
<wsdl:input message="tns:request" />
<wsdl:output message="tns:response" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="soapBinding" type="tns:portType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="execute">
<soap:operation soapAction="execute"/>
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="service">
<wsdl:port binding="tns:soapBinding" name="soapBinding">
<soap:address location="http://localhost:9080/dummy" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
JAR
Package info:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://schemas.xxx.nl/schema/v0010")
package nl.xxx.schema.jaxb.model;
MeldingType
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "MeldingType", propOrder = {
"code",
"melding"
})
public class MeldingType {
#XmlElement(required = true)
private String code;
#XmlElement(required = true)
private String melding;
public MeldingType() {
}
public MeldingType(final String code, final String melding) {
this.code = code;
this.melding = melding;
}
public String getCode() {
return this.code;
}
public String getMelding() {
return this.melding;
}
public void setCode(final String code) {
this.code = code;
}
public void setMelding(final String melding) {
this.melding = melding;
}
}
schema.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://schemas.xxx.nl/schema/v0010"
xmlns="http://schemas.xxx.nl/schema/v0010"
elementFormDefault="qualified">
<xsd:simpleType name="kenmerkType">
<xsd:restriction base="xsd:string">
<xsd:minLength value="12" />
<xsd:maxLength value="32" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="MeldingType">
<xsd:sequence>
<xsd:element name="code" type="xsd:string" minOccurs="1" />
<xsd:element name="melding" type="xsd:string" minOccurs="1" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Related
I need to implement an soap web service using firebase functions.
I found a module called soap-node soap-module-github which seems promising, since it integrates with express, and firebase says it uses express for http calls, but the problem, is I don't know how to integrate that module with firebase functions since, firebase functions are the handler of the http calls made by the clients, any help would be very helpfull.
Here is the code I have managed to create so far:
var fs = require('fs'),
soap = require('soap'),
express = require('express'),
lastReqAddress;
var server = express();
service = {
StockQuoteService: {
StockQuotePort: {
GetLastTradePrice: function (args, cb, soapHeader) {
if (soapHeader)
return {
price: soapHeader.SomeToken
};
if (args.tickerSymbol === 'trigger error') {
throw new Error('triggered server error');
} else if (args.tickerSymbol === 'Async') {
return cb({
price: 19.56
});
} else if (args.tickerSymbol === 'SOAP Fault v1.2') {
throw {
Fault: {
Code: {
Value: "soap:Sender",
Subcode: {
value: "rpc:BadArguments"
}
},
Reason: {
Text: "Processing Error"
}
}
};
} else if (args.tickerSymbol === 'SOAP Fault v1.1') {
throw {
Fault: {
faultcode: "soap:Client.BadArguments",
faultstring: "Error while processing arguments"
}
};
} else {
return {
price: 19.56
};
}
},
SetTradePrice: function (args, cb, soapHeader) {},
IsValidPrice: function (args, cb, soapHeader, req) {
lastReqAddress = req.connection.remoteAddress;
var validationError = {
Fault: {
Code: {
Value: "soap:Sender",
Subcode: {
value: "rpc:BadArguments"
}
},
Reason: {
Text: "Processing Error"
},
statusCode: 500
}
};
var isValidPrice = function () {
var price = args.price;
if (isNaN(price) || (price === ' ')) {
return cb(validationError);
}
price = parseInt(price, 10);
var validPrice = (price > 0 && price < Math.pow(10, 5));
return cb(null, {
valid: validPrice
});
};
setTimeout(isValidPrice, 10);
}
}
}
};
var wsdl = fs.readFileSync(__dirname + '/../wsdl/stockquote.wsdl', 'utf-8').toString();
server = express();
soapServer = soap.listen(server, '/stockquote', service, wsdl);
here is the stockquote.wsdl:
<wsdl:definitions name="StockQuote"
targetNamespace="http://example.com/stockquote.wsdl"
xmlns:tns="http://example.com/stockquote.wsdl"
xmlns:xsd1="http://example.com/stockquote.xsd"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<xsd:schema targetNamespace="http://example.com/stockquote.xsd" xmlns:xsd="http://www.w3.org/2000/10/XMLSchema">
<xsd:element name="TradePriceRequest">
<xsd:complexType>
<xsd:all>
<xsd:element name="tickerSymbol" type="string"/>
</xsd:all>
</xsd:complexType>
</xsd:element>
<xsd:element name="TradePrice">
<xsd:complexType>
<xsd:all>
<xsd:element name="price" type="float"/>
</xsd:all>
</xsd:complexType>
</xsd:element>
<xsd:element name="TradePriceSubmit">
<xsd:complexType>
<xsd:all>
<xsd:element name="tickerSymbol" type="string"/>
<xsd:element name="price" type="float"/>
</xsd:all>
</xsd:complexType>
</xsd:element>
<xsd:element name="valid" type="boolean"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="GetLastTradePriceInput">
<wsdl:part name="body" element="xsd1:TradePriceRequest"/>
</wsdl:message>
<wsdl:message name="GetLastTradePriceOutput">
<wsdl:part name="body" element="xsd1:TradePrice"/>
</wsdl:message>
<wsdl:message name="SetTradePriceInput">
<wsdl:part name="body" element="xsd1:TradePriceSubmit"/>
</wsdl:message>
<wsdl:message name="IsValidPriceInput">
<wsdl:part name="body" element="xsd1:TradePrice"/>
</wsdl:message>
<wsdl:message name="IsValidPriceOutput">
<wsdl:part name="body" element="xsd1:valid"/>
</wsdl:message>
<wsdl:portType name="StockQuotePortType">
<wsdl:operation name="GetLastTradePrice">
<wsdl:input message="tns:GetLastTradePriceInput"/>
<wsdl:output message="tns:GetLastTradePriceOutput"/>
</wsdl:operation>
<wsdl:operation name="SetTradePrice">
<wsdl:input message="tns:SetTradePriceInput"/>
</wsdl:operation>
<wsdl:operation name="IsValidPrice">
<wsdl:input message="tns:IsValidPriceInput"/>
<wsdl:output message="tns:IsValidPriceOutput"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="StockQuoteSoapBinding" type="tns:StockQuotePortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="GetLastTradePrice">
<soap:operation soapAction="http://example.com/GetLastTradePrice"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="SetTradePrice">
<soap:operation soapAction="http://example.com/SetTradePrice"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
</wsdl:operation>
<wsdl:operation name="IsValidPrice">
<soap:operation soapAction="http://example.com/IsValidPrice"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="StockQuoteService">
<wsdl:port name="StockQuotePort" binding="tns:StockQuoteSoapBinding">
<soap:address location="http://localhost:5002/stockquote"/>
</wsdl:port>
</wsdl:service>
I have googled very good I just don't find some path , I also searched google functions and their integration with soap, since firebase functions are just google cloud functions used for firebase
Peeking at the source code for node-soap, you should be able to directly pass the _requestListener to the Cloud Function's onRequest function:
exports.stockquote = functions.https.onRequest(soapServer._requestListener)
You are on the right path,
If you want the your GCF Path for the server to be http://myfunctions.domain.com/stockquote/
then your last line in the js file should be
soapServer = soap.listen(server, '/', service, wsdl) and then after that, in your Google Cloud Function index.js enter:
exports.stockquote = functions.https.onRequest(server)
You will have to make sure that your SOAP requests are going the endpoint with a trailing slash at the end. If you do not have control of the existing clients then you can add in your own URL handler that will look at the URL and add a / to the URL that your function received.
i.e.:
exports.stockquote = functions.https.onRequest( gcfURLHandler(server) );
where gcfURLHandleris defined as
function gcfURLHandler( handler ){
return (req, res ) => {
if( !req.url || !req.path ) {
req.url = "/" + (req.url || '');
}
handler( req, res )
}
}
figured this out from a comment here. (which also has other tips in the original code)
I was working on this last week and saw the unanswered question. Took a lot of digging around to finally figure it out. Hopefully this helps others looking to do the same!
You can use express with cloud functions now :
server = express();
server.listen(5002, function () {
soap.listen(server, '/stockquote', service, wsdl);
});
exports.stockquote = functions.https.onRequest(server);
Put the route in firebase.json :
"rewrites": [
{
"source": "/stockquote",
"function": "stockquote"
}
]
When testing on the client using javascript don't forget to change the endpoint to override localhost in wsdl :
var soap = require('soap');
var url = 'https://[your-project-id].firebaseapp.com/stockquote?wsdl';
var args = {tickerSymbol: 'some symbol', price: 100.0};
var options = {
'endpoint' : 'https://[your-project-id].firebaseapp.com/stockquote'
};
soap.createClient(url, options, function(err, client) {
if (err) throw err;
//print service in json
console.log(client.describe());
client.GetLastTradePrice(args, function(err, result) {
if(err)
console.log("err = "+ err.message);
console.log(result);
res.status(200).send(result);
});
});
i have a big problem calling a webService. When i did the import of the classes of the wsdl, i give this #WebService as a interface:
public void consultarProgramaCronicos(
#WebParam(name = "consultarProgramaCronicos", targetNamespace = "") ConsultarProgramaCronicosVO consultarProgramaCronicos,
#WebParam(name = "sip", targetNamespace = "", mode = WebParam.Mode.OUT) Holder<String> sip,
#WebParam(name = "programaCronico", targetNamespace = "", mode = WebParam.Mode.OUT) Holder<List<ProgramaCronicoVO>> programaCronico,
#WebParam(name = "marcaCronicidad", targetNamespace = "", mode = WebParam.Mode.OUT) Holder<List<MarcaCronicidadVO>> marcaCronicidad)
throws ConsultarProgramaCronicosFault;
So in my class, when i call this method i need to call it like this:
final Holder<String> sipHolder = new Holder<String>();
final Holder<List<ProgramaCronicoVO>> listProgramaCronico = new Holder<List<ProgramaCronicoVO>>();
final Holder<List<MarcaCronicidadVO>> listMarcaCronicidad = new Holder<List<MarcaCronicidadVO>>();
cronicos.consultarProgramaCronicos(cpc, sipHolder, listProgramaCronico, listMarcaCronicidad);
I pass an cpc object with the parameters i want, and i pass 3 holders for the answer.
The problem is i receive an object in the list but the primitive parameters of the object is "null".
This is the xml i receive:
<soap:Body wsu:Id="id-1995630735" xmlns:wsu="">
<consultarProgramaCronicosResponse xmlns="">
<sip>1703595</sip>
<marcaCronicidad>
<tipoMarca>CCC</tipoMarca>
<resultado>624</resultado>
<estadosalud>5</estadosalud>
<gravedad>1</gravedad>
<nivel>1</nivel>
<descripcionNivel>Texto cualquiera.</descripcionNivel>
</marcaCronicidad>
</consultarProgramaCronicosResponse></soap:Body>
In the "sip" holder i have the corrrect value. But In the List holder i have a list with one element of MarcaCronicidadV0, but all the string of this element are null.
Now, i show you a part of the .wsdl:
<xsd:complexType name="consultarProgramaCronicosResponse">
<xsd:sequence>
<xsd:element minOccurs="0" name="sip" type="xs:string"/>
<xsd:element maxOccurs="unbounded" minOccurs="0" name="programaCronico" type="tns:programaCronicoVO"/>
<xsd:element maxOccurs="unbounded" minOccurs="0" name="marcaCronicidad" type="tns:marcaCronicidadVO"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="marcaCronicidadVO">
<xsd:sequence>
<xsd:element name="tipoMarca" type="xs:string"/>
<xsd:element minOccurs="0" name="resultado" type="xs:string"/>
<xsd:element minOccurs="0" name="estadosalud" type="xs:string"/>
<xsd:element minOccurs="0" name="gravedad" type="xs:string"/>
<xsd:element minOccurs="0" name="nivel" type="xs:string"/>
</xsd:sequence>
</xsd:complexType>
And this is the class MarcaCronicidadVO:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "marcaCronicidadVO", namespace = "", propOrder = { "tipoMarca",
"resultado", "estadosalud", "gravedad", "nivel" })
public class MarcaCronicidadVO {
#XmlElement(required = true)
protected String tipoMarca;
protected String resultado;
protected String estadosalud;
protected String gravedad;
protected String nivel;
//getters y setters here
And in the end the "main object":
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "consultarProgramaCronicosResponse", namespace = "", propOrder = { "sip", "programaCronico","marcaCronicidad" })
public class ConsultarProgramaCronicosResponse {
protected String sip;
protected List<ProgramaCronicoVO> programaCronico;
protected List<MarcaCronicidadVO> marcaCronicidad;
public String getSip() {
return sip;
}
public void setSip(String value) {
this.sip = value;
}
public List<MarcaCronicidadVO> getMarcaCronicidad() {
if (marcaCronicidad == null) {
marcaCronicidad = new ArrayList<MarcaCronicidadVO>();
}
return this.marcaCronicidad;
}
public List<ProgramaCronicoVO> getProgramaCronico() {
if (programaCronico == null) {
programaCronico = new ArrayList<ProgramaCronicoVO>();
}
return this.programaCronico;
}
Why i dont have any error, and i recieve 3 correct holder, but in the holder list i have an element with attributes String with null value??????
Sorry by my english. Thank you!
I'm not sure why, but my problem is solved adding this parameter in the package-info class:
"elementFormDefault = XmlNsForm.QUALIFIED"
At the beginning i had this:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://myUrl....")
Now i put:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://myUrl....", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
And with this runs ok!
I hope to be helpful.
One of the Java webservices that I consume within my c# application has a returned type "marketCalendar" defined like following in the wsdl. WSDL's Target name space is http://www.xyz.com
<?xml version="1.0"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.xyz.com/wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.xyz.com/api" name="cscreen" targetNamespace="http://www.xyz.com/wsdl">
<wsdl:types>
<xsd:schema targetNamespace="http://www.xyz.com/api" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="getMarketCalendar">
<xsd:complexType>
</xsd:complexType>
</xsd:element>
<xsd:element name="marketCalendar">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="country" minOccurs="1" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="exchange" minOccurs="1" maxOccurs="unbounded">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="calendarDay" minOccurs="1" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="type" type="calendarDayType" use="required"/>
<xsd:attribute name="date" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="sessionID" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
<!--Get information on calendars -->
<wsdl:message name="getMarketCalendarRsp">
<wsdl:part name="marketCalendar" element="marketCalendar"/>
</wsdl:message>
<wsdl:message name="getMarketCalendarReq">
<wsdl:part name="getMarketCalendar" element="getMarketCalendar"/>
</wsdl:message>
<wsdl:portType name="Cscreen_PortType">
<wsdl:operation name="getMarketCalendarRequest">
<wsdl:input message="tns:getMarketCalendarReq"/>
<wsdl:output message="tns:getMarketCalendarRsp"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="Cscreen_Binding" type="tns:Cscreen_PortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getMarketCalendarRequest">
<soap:operation soapAction=""/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Cscreen_Service">
<wsdl:documentation>This WSDL file describes how to access the Cscreen OTC Equity Derivatives Price Discovery Platform through the SOAP API. This software was developed by Cinnober Financial Technology.</wsdl:documentation>
<wsdl:port name="Cscreen_Port" binding="tns:Cscreen_Binding">
<soap:address location="https://hostname/api/v16"/>
<!-- hostname can for example be stage.xyz.com and www.xyz.com -->
</wsdl:port>
</wsdl:service>
and corresponding C# proxy has following interpretation,
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.xyz.com")]
public partial class marketCalendar {
private marketCalendarCountry[] countryField;
private string sessionIDField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("country", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public marketCalendarCountry[] country {
get {
return this.countryField;
}
set {
this.countryField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string sessionID {
get {
return this.sessionIDField;
}
set {
this.sessionIDField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.xyz.com")]
public partial class marketCalendarCountry {
private marketCalendarCountryExchange[] exchangeField;
private string nameField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("exchange", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public marketCalendarCountryExchange[] exchange {
get {
return this.exchangeField;
}
set {
this.exchangeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string name {
get {
return this.nameField;
}
set {
this.nameField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.xyz.com")]
public partial class marketCalendarCountryExchange {
private marketCalendarCountryExchangeCalendarDay[] calendarDayField;
private string nameField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("calendarDay", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public marketCalendarCountryExchangeCalendarDay[] calendarDay {
get {
return this.calendarDayField;
}
set {
this.calendarDayField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string name {
get {
return this.nameField;
}
set {
this.nameField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.xyz.com")]
public partial class marketCalendarCountryExchangeCalendarDay {
private calendarDayType typeField;
private string dateField;
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public calendarDayType type {
get {
return this.typeField;
}
set {
this.typeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string date {
get {
return this.dateField;
}
set {
this.dateField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18408")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.xyz.com")]
public enum calendarDayType {
/// <remarks/>
HOLIDAY,
/// <remarks/>
EXPIRATION,
}
Problem# my c# application invokes a method GetMarketCalendar on the java webservice and does see the XML response coming in but can't deserialize it back into a marketCalendar response. So I started digging by using XGen generated class.
I noticed that for types country, exchange, calendarDay - c#'s default deserializer is comparing the namespace coming in the soap response to an empty string i.e.
if (((object) Reader.LocalName == (object)id84_country && (object) Reader.NamespaceURI == (object)id2_Item)) {
where incoming is Reader.NamespaceURI = "http://www.xyz.com" and id2_Item = Reader.NameTable.Add(#""); i.e. empty. Since comparision fails, deserialization also fails. Is there any work around?
Is this a C# XMLSerializer bug or there is something wrong with the marketCalendar definition in wsdl?
Here is a WSDL containing abstract global element named paramName:
<wsdl:definitions name="AdapterSessionManagerService" targetNamespace="http://companyname.org/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:tns="http://companyname.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://companyname.org/" version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<import namespace="http://companyname.org/adapter1/" schemaLocation="http://localhost:8081/adapters/AdapterSessionManager?xsd=adapter1/ElementName.xsd"/>
<import namespace="http://companyname.org/adapter2/" schemaLocation="http://localhost:8081/adapters/AdapterSessionManager?xsd=adapter2/ElementName.xsd"/>
<xs:element name="initAdapterSession" type="tns:initAdapterSession"/>
<xs:element name="initAdapterSessionResponse" type="tns:initAdapterSessionResponse"/>
<xs:complexType name="initAdapterSession">
<xs:sequence>
<xs:element name="adapterId" type="xs:long"/>
<xs:element name="callbackURL" type="xs:anyURI"/>
<xs:element name="adapterInputData" type="tns:AdapterInputDataType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="initAdapterSessionResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:long"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AdapterInputDataType">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="atomicParam" type="tns:AtomicParamType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="AtomicParamType">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="1" ref="tns:paramName"/>
<xs:element maxOccurs="1" minOccurs="1" name="paramValue" type="tns:AtomicParamValueType"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="AtomicParamValueType">
<xs:union memberTypes="xs:string xs:long xs:decimal xs:dateTime xs:boolean"/>
</xs:simpleType>
<xs:element abstract="true" name="paramName" type="xs:string"/>
</xs:schema>
</wsdl:types>
<wsdl:message name="initAdapterSessionResponse">
<wsdl:part element="tns:initAdapterSessionResponse" name="parameters"/>
</wsdl:message>
<wsdl:message name="initAdapterSession">
<wsdl:part element="tns:initAdapterSession" name="parameters"/>
</wsdl:message>
<wsdl:portType name="AdapterSessionManager">
<wsdl:operation name="initAdapterSession">
<wsdl:input message="tns:initAdapterSession" name="initAdapterSession"/>
<wsdl:output message="tns:initAdapterSessionResponse" name="initAdapterSessionResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="AdapterSessionManagerServiceSoapBinding" type="tns:AdapterSessionManager">
<soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="initAdapterSession">
<soap12:operation soapAction="" style="document"/>
<wsdl:input name="initAdapterSession">
<soap12:body use="literal"/>
</wsdl:input>
<wsdl:output name="initAdapterSessionResponse">
<soap12:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="AdapterSessionManagerService">
<wsdl:port binding="tns:AdapterSessionManagerServiceSoapBinding" name="AdapterSessionManagerPort">
<soap12:address location="http://localhost:8081/adapters/AdapterSessionManager"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
This element is intended to be subsituted in SOAP messages with same named elements defined in another namespaces. Here is an example of such definition:
<schema targetNamespace="http://companyname.org/adapter1/" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:inptypns="http://companyname.org/" xmlns:tns="http://companyname.org/adapter1/">
<import namespace="http://companyname.org/"/>
<element name="paramName" substitutionGroup="inptypns:paramName" type="tns:AtomicParamNameType"/>
<simpleType name="AtomicParamNameType">
<restriction base="string">
<enumeration value="foo"/>
<enumeration value="bar"/>
</restriction>
</simpleType>
</schema>
Here is Java code implementing this web-service including code generated by wsimport:
#WebService(wsdlLocation = "WEB-INF/wsdl/AdapterSessionManager.wsdl")
#BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public class AdapterSessionManager {
#WebMethod
public Long initAdapterSession(
#WebParam(name = "adapterId")
#XmlElement(required = true)
Long adapterId,
#WebParam(name = "callbackURL")
#XmlElement(required = true)
URL callbackURL,
#WebParam
#XmlElement(required = true)
AdapterInputDataType adapterInputData
) {...}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "AdapterInputDataType", propOrder = {
"atomicParam"
})
public class AdapterInputDataType {
protected List<AtomicParamType> atomicParam;
public List<AtomicParamType> getAtomicParam() {
if (atomicParam == null) {
atomicParam = new ArrayList<AtomicParamType>();
}
return this.atomicParam;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "AtomicParamType", propOrder = {
"paramName",
"paramValue"
})
public class AtomicParamType {
#XmlElementRef(name = "paramName", namespace = "http://companyname.org/", type = JAXBElement.class)
protected JAXBElement<?> paramName;
#XmlElement(required = true)
protected String paramValue;
public JAXBElement<?> getParamName() {
return paramName;
}
public void setParamName(JAXBElement<?> value) {
this.paramName = value;
}
public String getParamValue() {
return paramValue;
}
public void setParamValue(String value) {
this.paramValue = value;
}
}
#XmlRegistry
public class ObjectFactory {
private final static QName _ParamName_QNAME = new QName("http://companyname.org/", "paramName");
public ObjectFactory() {
}
public AtomicParamType createAtomicParamType() {
return new AtomicParamType();
}
public AdapterInputDataType createAdapterInputDataType() {
return new AdapterInputDataType();
}
#XmlElementDecl(namespace = "http://companyname.org/", name = "paramName")
public JAXBElement<String> createParamName(String value) {
return new JAXBElement<String>(_ParamName_QNAME, String.class, null, value);
}
}
#XmlType(name = "AtomicParamNameType", namespace = "http://companyname.org/adapter1/")
#XmlEnum
public enum AtomicParamNameType {
#XmlEnumValue("foo")
FOO("foo"),
#XmlEnumValue("bar")
BAR("bar");
private final String value;
AtomicParamNameType(String v) {
value = v;
}
public String value() {
return value;
}
public static AtomicParamNameType fromValue(String v) {
for (AtomicParamNameType c: AtomicParamNameType.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
#XmlRegistry
public class ObjectFactory {
private final static QName _ParamName_QNAME = new QName("http://companyname.org/adapter1/", "paramName");
public ObjectFactory() {
}
#XmlElementDecl(namespace = "http://companyname.org/adapter1/", name = "paramName", substitutionHeadNamespace = "http://companyname.org/", substitutionHeadName = "paramName")
public JAXBElement<AtomicParamNameType> createParamName(AtomicParamNameType value) {
return new JAXBElement<AtomicParamNameType>(_ParamName_QNAME, AtomicParamNameType.class, null, value);
}
}
I'm testing service with the following SOAP-request:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://companyname.org/" xmlns:ans="http://companyname.org/adapter1/">
<soapenv:Header/>
<soapenv:Body>
<ws:initAdapterSession>
<adapterId>123</adapterId>
<callbackURL>http://callback</callbackURL>
<adapterInputData>
<atomicParam>
<ans:paramName>foo</ans:paramName>
<paramValue>hello</paramValue>
</atomicParam>
</adapterInputData>
</ws:initAdapterSession>
</soapenv:Body>
</soapenv:Envelope>
I have the following UnmarshallException when an application server receives the request:
javax.xml.bind.UnmarshalException: unexpected element
(uri:"http://companyname.org/adapter1/", local:"paramName"). Expected
elements are <{http://companyname.org/}paramName>,<{}paramValue>
Looks like JAXB is unaware of paramName substitution defined in http://companyname.org/adapter1/ namespace. But I can't imagine why JAXB behaves in such a way. Where is my mistake? Any ideas?
You already know the answer: http://companyname.org/adapter1 is not the same as http://companyname.org/.
Your generated source explicitly indicates that the paramName element is defined in the http://companyname.org/ namespace with the following line.
#XmlElementRef(name = "paramName", namespace = "http://companyname.org/", type = JAXBElement.class)
protected JAXBElement<?> paramName;
So change the namespace attribute to http://companyname.org/adapter1 to allow JAXB properly unmarshal your payload
I'm new to Spring-WS and I have defined an endpoint based off a schema generated from JAXB annotated classes. However, when I try to access the endpoint via soapUI, I get the following error along with a 404 response code:
No endpoint mapping found for [SaajSoapMessage {clip}clipClaimRequest]
Any ideas as to what I'm doing wrong? Thanks for any help given.
The endpoint class:
#Endpoint
public class TestEndpoint {
#PayloadRoot(localPart = "clipClaimRequest", namespace = "clip")
#ResponsePayload
public CLIPClaimResponse registerClaim(#RequestPayload CLIPClaimRequest request) {
return new CLIPClaimResponse("Success", "test success");
}
}
The request/response classes (marshalled/unmarshalled via JAXB):
#XmlRootElement(name = "clipClaimRequest")
#XmlType(name = "CLIPClaimRequest")
public class CLIPClaimRequest {
private Claim claim;
#XmlElement(required = true)
public Claim getClaim() {
return claim;
}
public void setClaim(Claim claim) {
this.claim = claim;
}
}
And:
#XmlRootElement(name = "clipClaimResponse")
#XmlType(name = "CLIPClaimResponse")
public class CLIPClaimResponse {
private String code;
private String description;
public CLIPClaimResponse() {
}
public CLIPClaimResponse(String code, String desc) {
setCode(code);
setDescription(desc);
}
#XmlElement(required = true)
public String getCode() {
return code;
}
#XmlElement(required = true)
public String getDescription() {
return description;
}
public void setCode(String code) {
this.code = code;
}
public void setDescription(String description) {
this.description = description;
}
}
web.xml servlet configuration:
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<description>This is used by SpringWS to dynamically convert WSDL urls</description>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/clipClaimService/*</url-pattern>
</servlet-mapping>
spring-ws-servlet.xml configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<context:annotation-config />
<sws:annotation-driven />
<sws:dynamic-wsdl id="claimRegistration" portTypeName="CLIPClaimPort"
locationUri="/clipClaimService/" targetNamespace="clip">
<sws:xsd location="/WEB-INF/CLIP_Poc.xsd" />
</sws:dynamic-wsdl>
<bean id="jaxb2Marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<!-- list of classes... -->
</list>
</property>
<property name="schema" value="/WEB-INF/CLIP_PoC.xsd" />
</bean>
<bean id="marshallingPayloadMethodProcessor"
class="org.springframework.ws.server.endpoint.adapter.method.MarshallingPayloadMethodProcessor">
<constructor-arg ref="jaxb2Marshaller" />
<constructor-arg ref="jaxb2Marshaller" />
</bean>
<bean id="defaultMethodEndpointAdapter"
class="org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter">
<property name="methodArgumentResolvers">
<list>
<ref bean="marshallingPayloadMethodProcessor" />
</list>
</property>
<property name="methodReturnValueHandlers">
<list>
<ref bean="marshallingPayloadMethodProcessor" />
</list>
</property>
</bean>
</beans>
And finally, CLIP_PoC.xsd, the schema from which the WSDL was generated:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="clip" targetNamespace="clip" version="1.0">
<xs:element name="clipClaimRequest" type="CLIPClaimRequest"/>
<xs:element name="clipClaimResponse" type="CLIPClaimResponse"/>
<!-- type definitions for all the complex elements used... -->
</xs:schema>
I figured it out. I had forgotten to put: <context:component-scan base-package="my.base.package"/> in my spring-ws-servlet.xml file. This fixed it somehow.