I\'m consuming a SOAP webservice where I have to validate every xml post before call it.
So I\'m using:
finally I found an implementation that works for me.
The idea is:
Based on the question, I had to modify the XxxxMarshallerProperties class and the applicationContext-xxxx-base.xml files, only.
XxxxMarshallerProperties:
@Component
public class XxxxMarshallerProperties {
@Autowired
private ResourceLoader resourceLoader;
/**
* @return A new transformer.
* @throws TransformerFactoryConfigurationError
* @throws TransformerConfigurationException
*/
private Transformer newTransformer()
throws TransformerFactoryConfigurationError, TransformerConfigurationException {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
return transformer;
}
/**
* Load the wsdl into a dom Document. Used at:
* getSchemas
*
* @param wsdlUrl
* url where the WSDL is located at
* @param transformer
* used to load the document
* @return The wsdl dom Document
* @throws ParserConfigurationException
* @throws IOException
* @throws TransformerException
*/
private Document loadWsdlDoc(URL wsdlUrl, Transformer transformer)
throws ParserConfigurationException, IOException, TransformerException {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder schemaBuilder = docFactory.newDocumentBuilder();
Document wsdlDoc = schemaBuilder.newDocument();
BufferedReader wsdlReader = new BufferedReader(new InputStreamReader(wsdlUrl.openStream()));
Source source = new StreamSource(wsdlReader);
transformer.transform(source, new DOMResult(wsdlDoc));
return wsdlDoc;
}
/**
* Store into a map all namespaces defined on the wsdl. Used at:
* getSchemas
*
* @param defNode
* <wsdl:definitions> dom node where to look
* up.
* @return A map of namespace definition attributes. Format: [(nodeName,
* node)....]
*/
private Map getWsdlDefinedNamespaces(Element defNode) {
Map namespaces = new TreeMap<>();
NamedNodeMap defNodeAtt = defNode.getAttributes();
int defNodeAttSz = defNodeAtt.getLength();
for (int attIndex = 0; attIndex < defNodeAttSz; attIndex++) {
String ns = defNodeAtt.item(attIndex).getPrefix();
if ("xmlns".equals(ns)) {
namespaces.put(//
defNodeAtt.item(attIndex).getNodeName(), //
defNodeAtt.item(attIndex));
}
}
return namespaces;
}
/**
* Store into a map all the atributes present in a xsd schema node. Used at:
*
* addDefinitionNamespaces
*
* @param wsdlSchemaNode
* <s:schema> dom node where to look up.
* @return A map of attributes. Format: [(nodeName, node)....]
*/
private Map getCurrentSchemaAtt(Node wsdlSchemaNode) {
Map schemaXmlnss = new HashMap<>();
NamedNodeMap schemaNodeAtt = wsdlSchemaNode.getAttributes();
int schemaNodeAttSz = schemaNodeAtt.getLength();
for (int attIndex = 0; attIndex < schemaNodeAttSz; attIndex++) {
String nodeAttName = schemaNodeAtt.item(attIndex).getNodeName();
Node nodeAtt = ((NamedNodeMap) schemaNodeAtt).item(attIndex);
schemaXmlnss.put(nodeAttName, nodeAtt);
}
return schemaXmlnss;
}
/**
* Adds all non existing namespace definition attributes to a schema node in
* that schema node. If a namespace definition attribute name is found into
* schema node, it's not added to the current schema node attributes. Used
* at:
* getSchemas
*
* @param schemaNode
* <s:schema> dom node where to add the
* namespace definition attributes.
* @param namespaces
* map storing all namespace definition attributes.
*/
private void addDefinitionNamespaces(Node schemaNode, Map namespaces) {
Map currSchemaAttMap = getCurrentSchemaAtt(schemaNode);
for (Node xmlns : namespaces.values()) {
String nodeName = xmlns.getNodeName();
if (!currSchemaAttMap.containsKey(nodeName)) {
String namespaceURI = xmlns.getNamespaceURI();
String nodeValue = xmlns.getNodeValue();
((Element) schemaNode).setAttributeNS(namespaceURI, nodeName, nodeValue);
}
}
}
/**
* Update schema location by adding path. Used at:
* getSchemas
*
* @param schemaNode
* <s:schema> dom node to update its
* location.
* @return The updated schema.
*/
private DOMSource updateSchemaLocationByAddingPath(Node schemaNode) {
DOMSource schemaDomSource = new DOMSource(schemaNode);
NodeList noteList = schemaDomSource.getNode().getChildNodes();
for (int j = 0; j < noteList.getLength(); j++) {
if ("xsd:import".equals(noteList.item(j).getNodeName())) {
NamedNodeMap nodeMap = noteList.item(j).getAttributes();
for (int attIndex = 0; attIndex < nodeMap.getLength(); attIndex++) {
if ("schemaLocation".equals(nodeMap.item(attIndex).getNodeName())) {
nodeMap.item(attIndex).setNodeValue(nodeMap.item(attIndex).getNodeValue());
}
}
}
}
return schemaDomSource;
}
/**
* Transform a DOMSource schema into a spring's {@link Resource} to be
* attached to a {@link Jaxb2Marshaller}.
*
* @param schemaDomSource
* The schema to be transformed.
* @param transformer
* The transformer used.
* @return A spring's {@link Resource} interface.
* @throws TransformerException
*/
private Resource transformToResource(DOMSource schemaDomSource, Transformer transformer)
throws TransformerException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Result result = new StreamResult(outStream);
transformer.transform(schemaDomSource, result);
ByteArrayResource schemaResource = new ByteArrayResource(outStream.toByteArray());
return schemaResource;
}
/**
* Generate and retrieves all schemas contained into a wsdl file stored in
* the classpath, in an {@link Resource} array, to be attached to a
* {@link Jaxb2Marshaller} .
*
* @return An {@link Resource} array.
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
* @throws TransformerException
*/
public Resource[] getSchemas()
throws IOException, SAXException, ParserConfigurationException, TransformerException {
Resource[] schemaResources = null;
WebServiceClient wscAnnotation = Service.class.getAnnotation(WebServiceClient.class);
String wsdlLocationPath = wscAnnotation.wsdlLocation();
Resource wsdlResource = resourceLoader.getResource(wsdlLocationPath);
URL wsdlUri = wsdlResource.getURL();
Transformer transformer = newTransformer();
Document wsdlDoc = loadWsdlDoc(wsdlUri, transformer);
NodeList schemaNodes = wsdlDoc.getElementsByTagNameNS(XMLConstants.W3C_XML_SCHEMA_NS_URI, "schema");
int nrSchemas = schemaNodes.getLength();
if (nrSchemas > 0) {
Element defNode = wsdlDoc.getDocumentElement();
Map namespaces = getWsdlDefinedNamespaces(defNode);
schemaResources = new Resource[nrSchemas];
for (int i = 0; i < nrSchemas; i++) {
Node schemaNode = schemaNodes.item(i);
addDefinitionNamespaces(schemaNode, namespaces);
DOMSource schemaDomSource = updateSchemaLocationByAddingPath(schemaNode);
schemaResources[i] = transformToResource(schemaDomSource, transformer);
}
}
return schemaResources;
}
}
applicationContext-xxxx-base.xml:
Note that I'm using the Jaxb2Marshaller schemas, property (setSchemas), that allow me to set several schemas, instead of only one.