WSDLValidator.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */




package org.openspcoop2.core.registry.wsdl;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;

import org.apache.commons.lang.StringUtils;
import org.openspcoop2.core.registry.Message;
import org.openspcoop2.core.registry.MessagePart;
import org.openspcoop2.core.registry.Operation;
import org.openspcoop2.core.registry.PortType;
import org.openspcoop2.core.registry.constants.BindingStyle;
import org.openspcoop2.core.registry.constants.BindingUse;
import org.openspcoop2.core.registry.constants.CostantiRegistroServizi;
import org.openspcoop2.core.registry.driver.DriverRegistroServiziException;
import org.openspcoop2.core.registry.driver.IDAccordoFactory;
import org.openspcoop2.message.MessageUtils;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.constants.MessageType;
import org.openspcoop2.message.soap.SoapUtils;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.wsdl.WSDLException;
import org.openspcoop2.utils.xml.AbstractValidatoreXSD;
import org.openspcoop2.utils.xml.AbstractXMLUtils;
import org.openspcoop2.utils.xml.ValidatoreXSD;
import org.slf4j.Logger;
import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.Text;


/**
 * Classe utilizzata per validare i messaggi applicativi.
 *
 * @author Poli Andrea (apoli@link.it)
 * @author Lezza Aldo (lezza@openspcoop.org)
 * @author Lorenzo Nardi (nardi@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */

public class WSDLValidator {

	public static final String XMLSCHEMA_INSTANCE_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
	public static final String XMLSCHEMA_INSTANCE_LOCAL_NAME_TYPE = "type";
	private static final String PREFIX_RPC_AGGIUNTO = "op2RPC";
	
	/** OpenSPCoop2Message */
	private OpenSPCoop2Message openspcoop2Message;
	/** SOAPVersion */
	private MessageType messageType;
	/** Element */
	private Element element;
	/** WSDL Associato al servizio */
	private AccordoServizioWrapper accordoServizioWrapper = null;
	/** Logger */
	private Logger logger = null;
	/** XMLUtils */
	private AbstractXMLUtils xmlUtils = null;
	
	/** Nodo xsiType Aggiunto */
	private boolean gestioneXsiTypeRpcLiteral;
	private SOAPElement rpcChildElement;
	private QName rpcChildElementNamespaceAggiunto;
	private QName rpcChildElementXSITypeAggiunto;
	
	/** Nodo rpc root element */
	private boolean rpcAcceptRootElementUnqualified;
	
	/** XSI Type */
	private boolean validationXsdAddNamespaceXSITypeIfNotExists;
	private boolean validationRpcAddNamespaceXSITypeIfNotExists;
	private boolean validationDocumentAddNamespaceXSITypeIfNotExists;

	/** Prefix error */
	private boolean addPrefixError = true;
	public boolean isAddPrefixError() {
		return this.addPrefixError;
	}
	public void setAddPrefixError(boolean addPrefixError) {
		this.addPrefixError = addPrefixError;
	}
	
	/* ------ Costruttore -------------- */
	private static Element getEngineEnvelopeCatchException(OpenSPCoop2Message msg, boolean bufferMessageReadOnly, String idTransazione) throws WSDLException {
		try {
			boolean checkSoapBodyEmpty = true;
			return MessageUtils.getContentElement(msg, checkSoapBodyEmpty, bufferMessageReadOnly, idTransazione);
		}catch(Exception e){
			throw new WSDLException(e.getMessage(),e);
		}
	}
	public WSDLValidator(OpenSPCoop2Message msg,AbstractXMLUtils xmlUtils,AccordoServizioWrapper accordoServizioWrapper,Logger log,
			WSDLValidatorConfig config, boolean addPrefixError,
			boolean bufferMessageReadOnly, String idTransazione)throws WSDLException{
		this(msg.getMessageType(), getEngineEnvelopeCatchException(msg,bufferMessageReadOnly,idTransazione), xmlUtils, accordoServizioWrapper, log, 
				config, addPrefixError);
		this.openspcoop2Message = msg;
	}
	// Il costruttore sottostante non puo' sfruttare la funzionalita' addNamespaceXSITypeIfNotExists
	// per questo e' stato reso privato, poiche' tale funzionalita' richiede openspcoop2Message
	private WSDLValidator(MessageType messageType, Element element,AbstractXMLUtils xmlUtils,AccordoServizioWrapper accordoServizioWrapper,Logger log,
			WSDLValidatorConfig config, boolean addPrefixError)throws WSDLException{
		
		this.messageType = messageType;
		
		if(element==null){
			throw new WSDLException("Contenuto da validatore non fornito");
		}
		this.element = element;
		if(MessageType.SOAP_11.equals(this.messageType) || MessageType.SOAP_12.equals(this.messageType)){
			SOAPEnvelope envelope = (SOAPEnvelope) element;
			SOAPBody body = null;
			try{
				body = envelope.getBody();
			}catch(Exception e){
				if(this.logger!=null) {
					this.logger.error("SOAPEnvelope.getBody failed: "+e.getMessage(),e);
				}
				throw new WSDLException("SOAPEnvelope senza body");
			}
			
			if(body==null || (body.hasChildNodes()==false)){
				throw new WSDLException("SOAPBody non esistente");
			}
		}
		
		this.logger = log;
		this.xmlUtils = xmlUtils;
		this.accordoServizioWrapper = accordoServizioWrapper;
		
		this.gestioneXsiTypeRpcLiteral = config.isGestioneXsiTypeRpcLiteral();
		
		this.rpcAcceptRootElementUnqualified = config.isRpcAcceptRootElementUnqualified();
		
		this.validationXsdAddNamespaceXSITypeIfNotExists = config.isValidationXsdAddNamespaceXSITypeIfNotExists();
		this.validationRpcAddNamespaceXSITypeIfNotExists = config.isValidationRpcAddNamespaceXSITypeIfNotExists();
		this.validationDocumentAddNamespaceXSITypeIfNotExists = config.isValidationDocumentAddNamespaceXSITypeIfNotExists();
		
		this.addPrefixError = addPrefixError;
	}
	
	
	
	
	
	
	
	/* -------------- FINALIZE --------------------- */
	public void wsdlConformanceCheck_restoreOriginalDocument(){
		if(this.gestioneXsiTypeRpcLiteral && this.rpcChildElement!=null){
			try{
				if(this.rpcChildElementXSITypeAggiunto!=null){
					this.rpcChildElement.removeAttribute(this.rpcChildElementXSITypeAggiunto);
				}
			}catch(Exception e){
				this.logger.error("Errore durante l'eliminazione dell'attributo ["+this.rpcChildElementXSITypeAggiunto+"] dal rpc element");
			}
			try{
				if(this.rpcChildElementNamespaceAggiunto!=null){
					// alcune implementazioni usano l'uno o l'altro per eliminarlo
					this.rpcChildElement.removeAttribute(this.rpcChildElementNamespaceAggiunto);
					this.rpcChildElement.removeNamespaceDeclaration(PREFIX_RPC_AGGIUNTO);
				}
			}catch(Exception e){
				this.logger.error("Errore durante l'eliminazione dell'attributo ["+this.rpcChildElementNamespaceAggiunto+"] dal rpc element");
			}
		}
	}
	
	
	
	
	
	
	
	/* -------------- VALIDAZIONE XSD SENZA WSDL --------------------- */
	
	/**
	 * Validazione xsd
	 * 
	 * @param isRichiesta Indicazione sul tipo di messaggio applicativo da gestire
	 * @throws WSDLException (contiene codice e msg di errore)
	 */
	public void validateAgainstXSDSchema(boolean isRichiesta,String operationName) throws WSDLException,WSDLValidatorException{
		this.validateAgainstXSDSchema(isRichiesta, operationName, this.ptWsdlConformanceCheck, this.opWsdlConformanceCheck);
	}
	public void validateAgainstXSDSchema(boolean isRichiesta,String operationName,PortType portType,Operation operation) throws WSDLException,WSDLValidatorException{

		AbstractValidatoreXSD validatoreBodyApplicativo = null;
		try{
			if(this.accordoServizioWrapper.getSchema()!=null){
				validatoreBodyApplicativo = new ValidatoreXSD(this.accordoServizioWrapper.getSchema());
			}else{
				throw new Exception("Schema non costruito?");
			}
		}catch(Exception e){
			throw new WSDLException("Riscontrato errore durante la costruzione del validatore XSD per il contenuto applicativo: "+e.getMessage(),e);
		}
		
		
		/** ricerca ulteriore port type e operation in caso di validazione xsd */
		if(portType==null && this.accordoServizioWrapper.getNomePortType()!=null){
			// provo a cercarlo. Magari non e' stato fatto girare prima il metodo wsdlConformanceCheck
			// o magari volutamente non c'e' un wsdl perche' la validazione prevista e' XSD
			// in tal caso non e' un errore se il port type non e' presente.
			portType = this.accordoServizioWrapper.getPortType(this.accordoServizioWrapper.getNomePortType());
			if(portType==null){
				try{
					IDAccordoFactory idAccordoFactory = IDAccordoFactory.getInstance();
					String uriAccordo = idAccordoFactory.getUriFromIDAccordo(this.accordoServizioWrapper.getIdAccordoServizio());
					if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
						this.logger.debug("Port-Type ["+this.accordoServizioWrapper.getNomePortType()+"] non esistente nei wsdl registrati all'interno dell'accordo di servizio "+uriAccordo);
					}else{
						this.logger.debug("Servizio ["+this.accordoServizioWrapper.getNomePortType()+"] non esistente nell'accordo di servizio "+uriAccordo);
					}
				}catch(Exception e){
					this.logger.error("Errore durante l'emissione del log che indica la non esistenza del porttype/servizio ["+this.accordoServizioWrapper.getNomePortType()+
							"] nei wsdl registrati all'interno dell'accordo di servizio: "+e.getMessage(),e);
				}
			}
		}
		if(operation==null && operationName!=null && portType!=null){
			// provo a cercare l'operation. Magari non e' stato fatto girare prima il metodo wsdlConformanceCheck
			// o magari volutamente non c'e' un wsdl perche' la validazione prevista e' XSD
			// in tal caso non e' un errore se l'operation non e' presente.	
			for(int i=0; i<portType.sizeAzioneList();i++){
				if(operationName.equals(portType.getAzione(i).getNome())){
					operation = portType.getAzione(i);
					break;
				}
			}
		}
		
		
		StringBuilder errorMsgValidazioneXSD = new StringBuilder();
		
		
		/** Validazione XSD senza considerare gli usi e gli stili (rpc/document e literal/encoded) */
		if(portType==null || operation==null){
			this.logger.debug("Validazione XSD senza considerare il WSDLAccordoServizio e quindi senza considerare style (document/rpc) e gli use (literal/encoded)");
			
			List<Node> nodeList = new ArrayList<Node>();
			if(MessageType.SOAP_11.equals(this.messageType) || MessageType.SOAP_12.equals(this.messageType)){
				SOAPEnvelope envelope = (SOAPEnvelope) this.element;
				SOAPBody body = null;
				try{
					body = envelope.getBody();
				}catch(Exception e){
					// eccezione che non dovrebbe accadere. Lo stesso controllo viene fatto nel costruttore
					throw new RuntimeException(e.getMessage(),e);
				}
				org.w3c.dom.NodeList nl = body.getChildNodes();
				for(int i=0; i<nl.getLength(); i++){
					if ( (nl.item(i) instanceof Text) || (nl.item(i) instanceof Comment) ){
						continue;
					}
					nodeList.add(nl.item(i));
				}
			}
			else{
				nodeList.add(this.element);
			}
			
			for(int i=0; i<nodeList.size(); i++){
				String nomeElemento = null;
				String namespaceElemento = null;
				Node n = null;
				try{
					n = nodeList.get(i);
					nomeElemento = n.getLocalName();
					namespaceElemento = n.getNamespaceURI();
					
					// Bug Fix: OPPT-784: Validazione fallisce in presenza di xsi:type e normalizzazione da axiom
					if(this.openspcoop2Message!=null && this.validationXsdAddNamespaceXSITypeIfNotExists) {
						this.openspcoop2Message.addNamespaceXSITypeIfNotExists(n, this.element);
					}
					
					validatoreBodyApplicativo.valida(n,true);
				}catch(Exception e){
					if(errorMsgValidazioneXSD.length()==0){
						if(this.addPrefixError) {
							if(isRichiesta) {
								errorMsgValidazioneXSD.append("Request");
							}
							else {
								errorMsgValidazioneXSD.append("Response");
							}
							errorMsgValidazioneXSD.append("content not conform to XSD specification\n");
						}
					}else{
						errorMsgValidazioneXSD.append("\n");
					}
					if(namespaceElemento!=null){
						nomeElemento = "{"+namespaceElemento+"}"+nomeElemento;
					}
					errorMsgValidazioneXSD.append("(element "+nomeElemento+") "+e.getMessage());
					String elementNonValidato = null;
					try{
						if(n!=null) {
							elementNonValidato = this.xmlUtils.toString(n);
						}
					}catch(Exception eString){
						this.logger.error("Errore durante la conversione del Node in String: "+eString.getMessage(),eString);
					}
					this.logger.error("Validazione fallita (elemento "+nomeElemento+") ["+elementNonValidato+"]: "+e.getMessage(),e);
				}
			}
		}
		
		/** Validazione XSD che considerare gli usi e gli stili (rpc/document e literal/encoded) */
		else{
			this.logger.debug("Validazione XSD che considera il WSDLAccordoServizio (ho localizzato prima il port-type:"+portType.getNome()+" e l'operation:"+operation.getNome()+")");
			BindingStyle style = CostantiRegistroServizi.WSDL_STYLE_DOCUMENT;
			BindingUse use = CostantiRegistroServizi.WSDL_USE_LITERAL;
			String azione = operationName;
			
			try{
	
				if(portType.getStyle()!=null && ("".equals(portType.getStyle().getValue())==false) && 
						CostantiRegistroServizi.WSDL_STYLE_RPC.equals(portType.getStyle()))
					style = CostantiRegistroServizi.WSDL_STYLE_RPC;
				
				if(operation.getStyle()!=null && ("".equals(operation.getStyle().getValue())==false)){
					if(CostantiRegistroServizi.WSDL_STYLE_RPC.equals(operation.getStyle()))
						style = CostantiRegistroServizi.WSDL_STYLE_RPC;
					else if(CostantiRegistroServizi.WSDL_STYLE_DOCUMENT.equals(operation.getStyle()))
						style = CostantiRegistroServizi.WSDL_STYLE_DOCUMENT;
				}
				
			}catch(Exception e){
				if(errorMsgValidazioneXSD.length()>0){
					errorMsgValidazioneXSD.append("\n");
				}
				errorMsgValidazioneXSD.append("Error in recognizing wsdl style rpc/document '"+e.getMessage()+"'");
				this.logger.error("Validazione fallita durante il riconoscimento del wsdl style rpc/document: "+e.getMessage(),e);
			}
				
			try{
				if(isRichiesta){
					if(operation.getMessageInput()!=null && operation.getMessageInput().getUse()!=null &&
							("".equals(operation.getMessageInput().getUse().getValue())==false) &&
							CostantiRegistroServizi.WSDL_USE_ENCODED.equals(operation.getMessageInput().getUse()))
						use = CostantiRegistroServizi.WSDL_USE_ENCODED; 
				}else{
					if(operation.getMessageOutput()!=null && operation.getMessageOutput().getUse()!=null &&
							("".equals(operation.getMessageOutput().getUse().getValue())==false) &&
							CostantiRegistroServizi.WSDL_USE_ENCODED.equals(operation.getMessageOutput().getUse()))
						use = CostantiRegistroServizi.WSDL_USE_ENCODED; 
				}

			}catch(Exception e){
				if(errorMsgValidazioneXSD.length()>0){
					errorMsgValidazioneXSD.append("\n");
				}
				errorMsgValidazioneXSD.append("Error in recognizing wsdl use literal/encoded '"+e.getMessage()+"'");
				this.logger.error("Validazione fallita durante il riconoscimento del wsdl style literal/encoded: "+e.getMessage(),e);
			}
			
			if(errorMsgValidazioneXSD.length()==0){
				this.logger.debug("Validazione XSD con style["+style+"] e use["+use+"]...");
				
				List<Node> nodeList = new ArrayList<Node>();
				if(MessageType.SOAP_11.equals(this.messageType) || MessageType.SOAP_12.equals(this.messageType)){
					SOAPEnvelope envelope = (SOAPEnvelope) this.element;
					SOAPBody body = null;
					try{
						body = envelope.getBody();
					}catch(Exception e){
						// eccezione che non dovrebbe accadere. Lo stesso controllo viene fatto nel costruttore
						throw new RuntimeException(e.getMessage(),e);
					}
					org.w3c.dom.NodeList nl = body.getChildNodes();
					for(int i=0; i<nl.getLength(); i++){
						if ( (nl.item(i) instanceof Text) || (nl.item(i) instanceof Comment) ){
							continue;
						}
						nodeList.add(nl.item(i));
					}
				}
				else{
					nodeList.add(this.element);
				}
				
				if(CostantiRegistroServizi.WSDL_STYLE_RPC.equals(style)){
					int children=0;
					for(int i=0; i<nodeList.size(); i++){
						children++;
					}
					
					if(children>1){
						errorMsgValidazioneXSD.append("Operation '"+azione+"' (style RPC) bring more than one root element"); 
					}
				}

				if(errorMsgValidazioneXSD.length()==0){
					for(int i=0; i<nodeList.size(); i++){
						String nomeElemento = null;
						String namespaceElemento = null;
						Node n = null;
						Node nodo = null;
						Node nChild = null;
						try{
							n = nodeList.get(i);
							nomeElemento = n.getLocalName();
							namespaceElemento = n.getNamespaceURI();
							if(CostantiRegistroServizi.WSDL_USE_ENCODED.equals(use)){
								this.logger.debug("Validazione XSD con style["+style+"] e use["+use+"], richiede la pulizia dei xsi:types prima della validazione...");
								nodo = this.cleanXSITypes(n);
							}else{
								nodo = n;
							}
									
							if(CostantiRegistroServizi.WSDL_STYLE_RPC.equals(style)){
								this.logger.debug("Validazione XSD con style["+style+"] e use["+use+"] RPC Validation...");
								String nomeAtteso = azione;
								if(isRichiesta==false)
									nomeAtteso = azione+"Response";
								if(nomeAtteso.equals(nomeElemento)==false){
									throw new Exception("Root element ["+nomeElemento+"] non equivale all'operation name "+nomeAtteso +" (RPC Style)");
								}
								// Valido figli
								org.w3c.dom.NodeList nlChilds = nodo.getChildNodes();
								this.logger.debug("Valido figli size: "+nlChilds.getLength());
								for(int j=0; j<nlChilds.getLength(); j++){
									//this.logger.debug("Tipo["+j+"]: "+nlChilds.item(j).getClass().getName());
									//if (nlChilds.item(j) instanceof Text && ((Text) nlChilds.item(j)).getData().trim().length() == 0) { continue; }
									if ( (nlChilds.item(j) instanceof Text) || (nlChilds.item(j) instanceof Comment) ){
										continue;
									}
									nChild = nlChilds.item(j);
									
									// Bug Fix: OPPT-784: Validazione fallisce in presenza di xsi:type e normalizzazione da axiom
									if(this.openspcoop2Message!=null && this.validationRpcAddNamespaceXSITypeIfNotExists) {
										this.openspcoop2Message.addNamespaceXSITypeIfNotExists(nChild, this.element);
									}
									
									validatoreBodyApplicativo.valida(nChild,true);
								}
							}else{
								this.logger.debug("Validazione XSD con style["+style+"] e use["+use+"] Document Validation...");
								
								// Bug Fix: OPPT-784: Validazione fallisce in presenza di xsi:type e normalizzazione da axiom
								if(this.openspcoop2Message!=null && this.validationDocumentAddNamespaceXSITypeIfNotExists) {
									this.openspcoop2Message.addNamespaceXSITypeIfNotExists(nodo, this.element);
								}
								
								validatoreBodyApplicativo.valida(nodo,true);
							}
							
						}catch(Exception e){
							if(errorMsgValidazioneXSD.length()==0){
								if(this.addPrefixError) {
									if(isRichiesta) {
										errorMsgValidazioneXSD.append("Request");
									}
									else {
										errorMsgValidazioneXSD.append("Response");
									}
									errorMsgValidazioneXSD.append("content not conform to XSD specification\n");
								}
							}else{
								errorMsgValidazioneXSD.append("\n");
							}
							if(namespaceElemento!=null){
								nomeElemento = "{"+namespaceElemento+"}"+nomeElemento;
							}
							errorMsgValidazioneXSD.append("(element "+nomeElemento+") "+e.getMessage());
							
							String elementNonValidato = null;
							try{
								if(n!=null) {
									elementNonValidato = this.xmlUtils.toString(n);
								}
							}catch(Exception eString){
								this.logger.error("Errore durante la conversione del Node in String: "+eString.getMessage(),eString);
							}
							
							String elementNonValidato_cleanXSIType = null;
							try{
								if(nodo!=null) {
									elementNonValidato_cleanXSIType = this.xmlUtils.toString(nodo);
								}
							}catch(Exception eString){
								this.logger.error("Errore durante la conversione del Node (clean xsiType) in String: "+eString.getMessage(),eString);
							}
							
							String elementNonValidato_child = null;
							try{
								if(nChild!=null)
									elementNonValidato_child = this.xmlUtils.toString(nChild);
							}catch(Exception eString){
								this.logger.error("Errore durante la conversione del Node (clean xsiType) in String: "+eString.getMessage(),eString);
							}
							
							this.logger.error("Validazione fallita (elemento "+nomeElemento+") originale["+
									elementNonValidato+"] cleanXsiType["+
									elementNonValidato_cleanXSIType+"] nChild["+
									elementNonValidato_child+"]: "+e.getMessage(),e);
						}
					}
				}
			}
		}
		if(errorMsgValidazioneXSD.length()>0){
			throw new WSDLValidatorException(errorMsgValidazioneXSD.toString());
		}
	}

	
	
	
	
	
	
	
	
	
	
	/* -------------- VALIDAZIONE XSD CON WSDL --------------------- */
	
	/**
	 * Validazione WSDL
	 * 
	 * @param isRichiesta Indicazione sul tipo di messaggio applicativo da gestire
	 * 
	 */
	private PortType ptWsdlConformanceCheck = null;
	private Operation opWsdlConformanceCheck = null;
	public PortType getPtWsdlConformanceCheck() {
		return this.ptWsdlConformanceCheck;
	}
	public Operation getOpWsdlConformanceCheck() {
		return this.opWsdlConformanceCheck;
	}
//	public void wsdlConformanceCheck(boolean isRichiesta,String soapAction,String operationName) throws WSDLValidatorException {
//		this.wsdlConformanceCheck(isRichiesta, soapAction, operationName, true, false);
//	}
	public void wsdlConformanceCheck(boolean isRichiesta,String soapAction,String operationName,boolean throwSOAPActionException,boolean logErrorAsDebug) throws WSDLValidatorException {
				
		String portType = this.accordoServizioWrapper.getNomePortType();
		
		if(portType!=null){
			
			// l'accordo di servizio parte specifica e' stato collegato ad un port type nella parte comune.
			// In tal caso uso la validazione ottimale rispetto al port type ed all'operation
			
			// l'azione busta deve essere obbligatoriamente presente.
			if(operationName==null){
				throw new WSDLValidatorException("Operation name undefined");
			}
			
			this.logger.info("WSDL, effettuo validazione wsdlConformanceCheck ottimale sia con port type che operation ...");
			this._engineWsdlConformanceCheck(isRichiesta, soapAction, portType, operationName, throwSOAPActionException, logErrorAsDebug);
			
			// se la validazione ha avuto successo salvo il pt e l'operation
			this.ptWsdlConformanceCheck = this.accordoServizioWrapper.getPortType(portType);
			for(int i=0; i<this.ptWsdlConformanceCheck.sizeAzioneList(); i++){
				Operation operationAS = this.ptWsdlConformanceCheck.getAzione(i);
				if (operationAS.getNome().equals(operationName)) {
					this.opWsdlConformanceCheck = operationAS;
					break;
				}
			}
		}
		else{
			
			// l'accordo di servizio parte specifica non e' stato collegato ad un port type nella parte comune.	
			// effettuo una validazione meno stringente.
				
			if(operationName!=null){
				
				// *** cerco l'operation all'interno del wsdl. Se esiste una o piu' operation con tale nome, proviamo a validare il messaggio. ***
				this.logger.info("WSDL, effettuo validazione wsdlConformanceCheck cercando una qualche operation con nome ["+operationName+"]...");
				StringBuilder bfEccezione = new StringBuilder();
				boolean operationFound = _engineWsdlConformanceCheckAll(isRichiesta, soapAction, operationName, bfEccezione, throwSOAPActionException, logErrorAsDebug);
				if(operationFound){
					if(bfEccezione.length()>0){
						throw new WSDLValidatorException(bfEccezione.toString());
					}
					else{
						return;
					}
				}else{
					try{
						IDAccordoFactory idAccordoFactory = IDAccordoFactory.getInstance();
						String uriAccordo = idAccordoFactory.getUriFromIDAccordo(this.accordoServizioWrapper.getIdAccordoServizio());
						if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
							throw new WSDLValidatorException("Operation '"+operationName+"' undefined in the WSDL specification '"+uriAccordo+"'");
						}else{
							throw new WSDLValidatorException("Operation '"+operationName+"' undefined in the API specification '"+uriAccordo+"'");
						}
					}catch(DriverRegistroServiziException de){
						String msgErrore = "Errore durante la registrazione del messaggio di errore Operation ["+operationName+"] non trovata in alcun port-type esistente nei wsdl registrati all'interno dell'accordo di servizio";
						if(logErrorAsDebug){
							this.logger.debug(msgErrore);
						}else{
							this.logger.error(msgErrore,de);
						}
						if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
							throw new WSDLValidatorException("Operation '"+operationName+"' undefined in the WSDL specification");
						}else{
							throw new WSDLValidatorException("Operation '"+operationName+"' undefined in the API specification");
						}
					}
				}
			}
			else{
			
				// *** 
				//     non avendo trovato una azione che matchava con il nome dell'operation name, 
				//     valido rispetto a tutti i messaggi possibili, rispettando pero' il ruolo tra richiesta e risposta 
				//  ***		
				StringBuilder bfEccezione = new StringBuilder();
				this.logger.info("WSDL, effettuo validazione wsdlConformanceCheck utilizzando una qualunque operation ...");
				_engineWsdlConformanceCheckAll(isRichiesta, soapAction, null, bfEccezione, throwSOAPActionException, logErrorAsDebug);
				if(bfEccezione.length()>0){
					throw new WSDLValidatorException(bfEccezione.toString());
				}
				else{
					return;
				}
				
			}
			
		}
		
	}
	
	private boolean _engineWsdlConformanceCheckAll(boolean isRichiesta,String soapAction,String operationName, StringBuilder bfEccezione,boolean throwSOAPActionException,boolean logErrorAsDebug) throws WSDLValidatorException{
		
		try{
		
			IDAccordoFactory idAccordoFactory = IDAccordoFactory.getInstance();
			String uriAccordo = idAccordoFactory.getUriFromIDAccordo(this.accordoServizioWrapper.getIdAccordoServizio());
			
			PortType [] pts = this.accordoServizioWrapper.getPortTypeList();
			if(pts==null || pts.length<=0){
				if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
					throw new WSDLValidatorException("PortTypes undefined in the WSDL specification '"+uriAccordo+"'");
				}else{
					throw new WSDLValidatorException("PortTypes undefined in the API specification '"+uriAccordo+"'");
				}
			}
			boolean operationFound = false;
			for (int i = 0; i < pts.length; i++) {
				PortType pt = pts[i];
				List<Operation> ops = pt.getAzioneList();
				if(ops==null || ops.size()<=0){
					if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
						throw new WSDLValidatorException("Operations undefined in PortType '"+pt.getNome()+"' of the WSDL specification '"+uriAccordo+"'");
					}else{
						throw new WSDLValidatorException("Operations undefined in Service '"+pt.getNome()+"' of the API specification '"+uriAccordo+"'");
					}
				}
				boolean validazioneCompletataConSuccesso = false;
				for (Operation operation : ops) {
					
					boolean verify = false;
					if(operationName==null){
						verify = true;
					}
					else{
						verify = (operation.getNome().equals(operationName));
						if(verify){
							operationFound = true;
						}
					}
					
					if(verify){
						try{
							this.logger.info("WSDL, effettuo validazione wsdlConformanceCheck per operation ["+operation.getNome()+"] del port type ["+pt.getNome()+"]...");
							this._engineWsdlConformanceCheck(isRichiesta, soapAction, pt.getNome(), operation.getNome(), throwSOAPActionException, logErrorAsDebug);
							
							// se la validazione ha avuto successo salvo il pt e l'operation
							this.ptWsdlConformanceCheck = pt;
							this.opWsdlConformanceCheck = operation;
							
							validazioneCompletataConSuccesso = true;
							break;
							
						}catch(WSDLValidatorException exception){
//							if(bfEccezione.length()>0){
							bfEccezione.append("\n");
							//}
							bfEccezione.append("[Tentativo validazione come PortType:").
								append(pt.getNome()).
								append(" Operation:").
								append(operation.getNome()).
								append(" fallito]: ").
								append(exception.getMessage());
						}
					}
				}
				
				if(validazioneCompletataConSuccesso){
					break;
				}
			}
			
			return operationFound;
			
		}catch(WSDLValidatorException e){
			throw e;
		}catch(Exception e){
			// Si entra in questo catch solo in caso di bug
			String msgErrore = "Validazione WSDL ("+isRichiesta+") fallita: "+e.getMessage();
			if(logErrorAsDebug){
				this.logger.debug(msgErrore);
			}else{
				this.logger.error(msgErrore,e);
			}
			throw new WSDLValidatorException("WSDL Validation 'all-"+(isRichiesta?"request":"response")+"' failed: "+e.getMessage(),e);
		}
	}
	
	private void _engineWsdlConformanceCheck(boolean isRichiesta,String soapAction,String portType,String operation,boolean throwSOAPActionException,boolean logErrorAsDebug) throws WSDLValidatorException {
		
		String errorMsgValidazioneXSD = null;
		try{
			
			SOAPEnvelope envelope = null;
			SOAPBody body = null;
			if(MessageType.SOAP_11.equals(this.messageType) || MessageType.SOAP_12.equals(this.messageType)){
				envelope = (SOAPEnvelope) this.element;
				try{
					body = envelope.getBody();
				}catch(Exception e){
					// eccezione che non dovrebbe accadere. Lo stesso controllo viene fatto nel costruttore
					throw new RuntimeException(e.getMessage(),e);
				}
			}
			else{
				throw new Exception("Tipo di validazione non supportata con Service Binding REST e tipologia messaggio: "+this.messageType.getMessageVersionAsString());
			}
			
	
			// cerco port-type
			BindingStyle style = CostantiRegistroServizi.WSDL_STYLE_DOCUMENT;
			BindingUse use = CostantiRegistroServizi.WSDL_USE_LITERAL;
			String namespaceRPC = null;
			Node rpcElement = null;
			IDAccordoFactory idAccordoFactory = IDAccordoFactory.getInstance();
			String uriAccordo = idAccordoFactory.getUriFromIDAccordo(this.accordoServizioWrapper.getIdAccordoServizio());
			PortType portTypeAS = this.accordoServizioWrapper.getPortType(portType);
			if(portTypeAS==null){
				if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
					throw new WSDLValidatorException("PortType ["+portType+"] undefined in the WSDL specification '"+uriAccordo+"'");
				}else{
					throw new WSDLValidatorException("Service ["+portType+"] undefined in the API specification '"+uriAccordo+"'");
				}
			}
			if(portTypeAS.getStyle()!=null && ("".equals(portTypeAS.getStyle().getValue())==false) && 
					CostantiRegistroServizi.WSDL_STYLE_RPC.equals(portTypeAS.getStyle()))
				style = CostantiRegistroServizi.WSDL_STYLE_RPC;
			
			//si itera sulle operazioni del portType perche' potrebbe esserci
			//overload di operazioni
			if(portTypeAS.sizeAzioneList()<=0)
				throw new Exception("operations per il port type ["+portType+"] non presenti");
			boolean matchingNameOperation = false, matchingArgomentsOperation = false;
			String soapActionWSDL = null;
			StringBuilder eccezioni = new StringBuilder();
			StringBuilder eccezioneActionMatch = new StringBuilder();
			for(int i=0; i<portTypeAS.sizeAzioneList(); i++){
				Operation operationAS = portTypeAS.getAzione(i);
				if (operationAS.getNome().equals(operation)) {
					matchingNameOperation = true;
					// Prendo la definizione del messaggio di input se e' una richiesta, di output se e' una risposta
					Message argumentsOperation = isRichiesta ? operationAS.getMessageInput() : operationAS.getMessageOutput();
					
					if(operationAS.getStyle()!=null && ("".equals(operationAS.getStyle().getValue())==false)){
						if(CostantiRegistroServizi.WSDL_STYLE_RPC.equals(operationAS.getStyle()))
							style = CostantiRegistroServizi.WSDL_STYLE_RPC;
						else if(CostantiRegistroServizi.WSDL_STYLE_DOCUMENT.equals(operationAS.getStyle()))
							style = CostantiRegistroServizi.WSDL_STYLE_DOCUMENT;
					}
					
					if(argumentsOperation!=null && argumentsOperation.getUse()!=null &&
							("".equals(argumentsOperation.getUse().getValue())==false) &&
							CostantiRegistroServizi.WSDL_USE_ENCODED.equals(argumentsOperation.getUse()))
						use = CostantiRegistroServizi.WSDL_USE_ENCODED; 
					
					if(CostantiRegistroServizi.WSDL_STYLE_RPC.equals(style)){
						if(argumentsOperation!=null && argumentsOperation.getSoapNamespace()!=null &&
								(!"".equals(argumentsOperation.getSoapNamespace()))){
							namespaceRPC = argumentsOperation.getSoapNamespace();
						}
					}
										
					this.logger.debug("WSDL, esamino operation["+operation+"] con style["+style+"] e use["+use+"] (RPCNamespace:"+namespaceRPC+") ...");
					
					// il controllo sul nome dell'operation non basta.
					// Vi puo' essere overriding del metodo per parametri diversi
					// controllo matching dei parametri 
					org.w3c.dom.NodeList nodiContenutoApplicativo = null;
					Node nodoPossiedeContenutoApplicativo = null; // body nel caso document, rpc-element nel caso rpc
					if(CostantiRegistroServizi.WSDL_STYLE_RPC.equals(style)){
						// RPC
						String nomeAtteso = operation;
						if(isRichiesta==false)
							nomeAtteso = operation+"Response";
						SOAPElement childRPCElement = SoapUtils.getNotEmptyFirstChildSOAPElement(body);
						if(childRPCElement==null){
							this.logger.debug("WSDL, esamino operation["+operation+"] con style["+style+"] e use["+use+"]: Root element RCP non trovato rispetto all'operation name "+nomeAtteso +" (RPC Style)");
							continue;
						}
						if(nomeAtteso.equals(childRPCElement.getLocalName())==false){
							this.logger.debug("WSDL, esamino operation["+operation+"] con style["+style+"] e use["+use+"]: Root element ["+childRPCElement.getLocalName()+"] non equivale all'operation name "+nomeAtteso +" (RPC Style)");
							continue;
						}
						
						nodiContenutoApplicativo = body.getChildNodes();
						for(int ii=0;ii<nodiContenutoApplicativo.getLength();ii++){
//							if (!(nodiContenutoApplicativo.item(ii) instanceof Text && 
//									((Text) nodiContenutoApplicativo.item(ii)).getData().trim().length() == 0)) { 
							if (! ( (nodiContenutoApplicativo.item(ii) instanceof Text) || (nodiContenutoApplicativo.item(ii) instanceof Comment) )){
								nodoPossiedeContenutoApplicativo = nodiContenutoApplicativo.item(ii);
								rpcElement = nodoPossiedeContenutoApplicativo;
								nodiContenutoApplicativo = nodoPossiedeContenutoApplicativo.getChildNodes();
								break;
							}
						}
						
					}else{
						// Document
						nodiContenutoApplicativo = body.getChildNodes();
						nodoPossiedeContenutoApplicativo = body;
					}
					
					int sizeArgumentsOperation = 0;
					if(argumentsOperation!=null){
						sizeArgumentsOperation = argumentsOperation.sizePartList();
					}
					int nodiContenutoApplicativoLength = 0;
					StringBuilder nodiMessaggioErrore = new StringBuilder();
					for(int ii=0;ii<nodiContenutoApplicativo.getLength();ii++){
//						if (!(nodiContenutoApplicativo.item(ii) instanceof Text && 
//								((Text) nodiContenutoApplicativo.item(ii)).getData().trim().length() == 0)) { 
						if (! ( (nodiContenutoApplicativo.item(ii) instanceof Text) || (nodiContenutoApplicativo.item(ii) instanceof Comment) )){
							
							if(nodiMessaggioErrore.length()>0){
								nodiMessaggioErrore.append(", ");
							}
							Node n = nodiContenutoApplicativo.item(ii);
							RootElementBody rootElementBody = new RootElementBody(envelope, nodoPossiedeContenutoApplicativo, 
									CostantiRegistroServizi.WSDL_STYLE_RPC.equals(style), n);
							nodiMessaggioErrore.append(rootElementBody.toString());
							
							nodiContenutoApplicativoLength++;
							continue;
						}
					}
					if(sizeArgumentsOperation!=nodiContenutoApplicativoLength){
						
						if(eccezioneActionMatch.length()>0){
							eccezioneActionMatch.append("\n");
						}
						eccezioneActionMatch.append("Found "+nodiContenutoApplicativoLength+" parameter"+(nodiContenutoApplicativoLength>1?"s":"")+": ");
						eccezioneActionMatch.append(nodiMessaggioErrore.toString());
						
						this.logger.debug("WSDL, esamino operation["+operation+"] con style["+style+"] e use["+use+"]: Argomenti attesi["+sizeArgumentsOperation+"], trovati nel body["+nodiContenutoApplicativo.getLength()+"]");
						continue;
					}
										
					String tipo  = "output";
					if(isRichiesta)
						tipo = "input";
					
					if(argumentsOperation!=null && argumentsOperation.sizePartList()>0){
						
						// Mi conservo gli elementi presenti nel body
						List<RootElementBody> elementRootBody = new ArrayList<RootElementBody>();
						StringBuilder bodyElements = new StringBuilder();
						int numeroBodyElements = 0;
						int realIndexBody = 0;
						for(int indexBody = 0 ; indexBody<nodiContenutoApplicativo.getLength(); indexBody++){
//							if (nodiContenutoApplicativo.item(indexBody) instanceof Text && 
//									((Text) nodiContenutoApplicativo.item(indexBody)).getData().trim().length() == 0) { 
							if ( (nodiContenutoApplicativo.item(indexBody) instanceof Text) || (nodiContenutoApplicativo.item(indexBody) instanceof Comment) ){
								continue;
							}
							
							Node n = nodiContenutoApplicativo.item(indexBody);
							
							RootElementBody rootElementBody = new RootElementBody(envelope, nodoPossiedeContenutoApplicativo, 
									CostantiRegistroServizi.WSDL_STYLE_RPC.equals(style), n);
							elementRootBody.add(rootElementBody);
							
							if(realIndexBody>0)
								bodyElements.append(",");
							realIndexBody++;
							bodyElements.append(rootElementBody.toString());
						}
						numeroBodyElements = elementRootBody.size();
						
						int soapBodyArguments = nodiContenutoApplicativoLength;
						//per ogni tipo si itera
						int wsdlIndex=0;
						for( ; wsdlIndex<argumentsOperation.sizePartList(); wsdlIndex++){
							if (wsdlIndex == soapBodyArguments) { 
								//più oggetti definiti nel WSDL di quanti ce ne siano nel messaggio
								this.logger.debug("WSDL, esamino operation["+operation+"] con style["+style+"] e use["+use+"]: Più oggetti definiti nel WSDL di quanti ce ne siano nel messaggio");
								continue;
							}
							MessagePart argument = argumentsOperation.getPart(wsdlIndex);
							String nomeElementAtteso = null;
							String namespaceElementAtteso = null;
							String tipoXSIAtteso = null;
							boolean validazioneTipologiaElement = (argument.getElementName()!=null);
							String argomentoAtteso = null;
							if(argument.getElementName()!=null){
								nomeElementAtteso = argument.getElementName();
								namespaceElementAtteso = argument.getElementNamespace();
							}
							else{
								nomeElementAtteso = argument.getName(); // definito nel message del wsdl
								namespaceElementAtteso = argument.getTypeNamespace();
								tipoXSIAtteso = argument.getTypeName();
							}
							argomentoAtteso = RootElementBody.toString(nomeElementAtteso, namespaceElementAtteso, tipoXSIAtteso);
							boolean find = false;
							for(int indexBody = 0 ; indexBody<elementRootBody.size(); indexBody++){
								RootElementBody r = elementRootBody.get(indexBody);
								if(validazioneTipologiaElement){
									if(nomeElementAtteso.equals(r.getLocalName()) && namespaceElementAtteso.equals(r.getNamespace())){
										find = true;
										elementRootBody.remove(indexBody);
										continue;
									}
									else if(CostantiRegistroServizi.WSDL_USE_ENCODED.equals(use)){
										if(nomeElementAtteso.equals(r.getLocalName()) && namespaceElementAtteso.equals(r.getNamespaceElementoCheContieneXSIType())){
											find = true;
											elementRootBody.remove(indexBody);
											continue;
										}
									}
								}
								else{
									if(nomeElementAtteso.equals(r.getLocalName()) && 
											namespaceElementAtteso.equals(r.getNamespace()) &&
											tipoXSIAtteso.equals(r.getXsiType())){
										find = true;
										elementRootBody.remove(indexBody);
										continue;
									}
								}
							}
	
							if (!find) { //tipi non concordi nella sequenza
								boolean error = true;
								if(CostantiRegistroServizi.WSDL_STYLE_RPC.equals(style) && CostantiRegistroServizi.WSDL_USE_LITERAL.equals(use) &&
										tipoXSIAtteso!=null &&
										elementRootBody.size()==1 && // elementi nel body sono uno
										argumentsOperation.sizePartList()==1 // elementi attesi nel wsdl uno 
										){
									
									SOAPElement rpcOperation = SoapUtils.getNotEmptyFirstChildSOAPElement(body);
									if(rpcOperation!=null){
										SOAPElement childRpc = SoapUtils.getNotEmptyFirstChildSOAPElement(rpcOperation);
										if(this.gestioneXsiTypeRpcLiteral && 
												childRpc!=null && nomeElementAtteso!=null && nomeElementAtteso.equals(childRpc.getLocalName())){
											try{
//												System.out.println("PRIMA: "+org.openspcoop2.message.OpenSPCoop2MessageFactory.getMessageFactory().createEmptySOAPMessage(SOAPVersion.SOAP11).
//														getAsString(childRpc, false));
												
												QName namespaceId = new QName("xmlns:"+PREFIX_RPC_AGGIUNTO);
												childRpc.addAttribute(namespaceId, namespaceElementAtteso);
												this.rpcChildElementNamespaceAggiunto = namespaceId;
											
												QName id = new QName(XMLSCHEMA_INSTANCE_NAMESPACE,XMLSCHEMA_INSTANCE_LOCAL_NAME_TYPE,"xsi");
												String value = PREFIX_RPC_AGGIUNTO+":"+tipoXSIAtteso;
												childRpc.addAttribute(id, value);
												this.rpcChildElementXSITypeAggiunto = id;
											
//												System.out.println("DOPO: "+org.openspcoop2.message.OpenSPCoop2MessageFactory.getMessageFactory().createEmptySOAPMessage(SOAPVersion.SOAP11).
//														getAsString(childRpc, false));
												error= false;
												this.rpcChildElement = childRpc;
											}catch(Exception e){
												this.logger.error("Errore durante la registrazione degli attributi richiesti per la validazione rpc con type: "+e.getMessage(),e);
												try{
													if(this.rpcChildElementNamespaceAggiunto!=null){
														// ripulisco
														// alcune implementazioni usano l'uno o l'altro per eliminarlo
														this.rpcChildElement.removeAttribute(this.rpcChildElementNamespaceAggiunto);
														this.rpcChildElement.removeNamespaceDeclaration(PREFIX_RPC_AGGIUNTO);
													}
												}catch(Exception eClose){
													// ignore
												}
											}
										}
									}
								}
								if(error){
									eccezioni.append("\nRequired "+tipo+" parameter '"+argomentoAtteso+"' undefined in "+numeroBodyElements+" body root-element("+(numeroBodyElements>1?"s":"")+") founded: "+bodyElements);
									this.logger.debug("WSDL, esamino operation["+operation+"] con style["+style+"] e use["+use+"]: Atteso "+argomentoAtteso+" body "+bodyElements);
									break;
								}
							}
						}
						if (wsdlIndex == soapBodyArguments) {
							soapActionWSDL = operationAS.getSoapAction();
							matchingArgomentsOperation = true;
							continue;
						}
					}

				} //fine if
			}

			if (!matchingArgomentsOperation) {
				if (matchingNameOperation) {
					if(eccezioni.length()>0){
						if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
							throw new WSDLValidatorException("Invalid "+(isRichiesta?"request":"response")+" by WSDL specification '"+uriAccordo+"' (port-type:"+portType+", operation:"+operation+", style:"+style+", use:"+use+"): "+eccezioni.toString());
						}else{
							throw new WSDLValidatorException("Invalid "+(isRichiesta?"request":"response")+" by API specification '"+uriAccordo+"' (service:"+portType+", operation:"+operation+", style:"+style+", use:"+use+"): "+eccezioni.toString());
						}
					}
					else if(eccezioneActionMatch.length()>0){
						StringBuilder bfMessage = new StringBuilder();
						for(int i=0; i<portTypeAS.sizeAzioneList(); i++){
							Operation operationAS = portTypeAS.getAzione(i);
							if (operationAS.getNome().equals(operation)) {
								Message argumentsOperation = isRichiesta ? operationAS.getMessageInput() : operationAS.getMessageOutput();
								int length = 0;
								if(argumentsOperation!=null && argumentsOperation.getPartList()!=null){
									length = argumentsOperation.getPartList().size();
								}
								bfMessage.append("\n");
								bfMessage.append("Expected "+length+" parameter"+(length>1?"s":"")+": ");
								if(length>0){
									for (int j = 0; j < length; j++) {
										MessagePart argument = argumentsOperation.getPart(j);
										String nomeElementAtteso = null;
										String namespaceElementAtteso = null;
										String tipoXSIAtteso = null;
										String argomentoAtteso = null;
										if(argument.getElementName()!=null){
											nomeElementAtteso = argument.getElementName();
											namespaceElementAtteso = argument.getElementNamespace();
										}
										else{
											nomeElementAtteso = argument.getName(); // definito nel message del wsdl
											namespaceElementAtteso = argument.getTypeNamespace();
											tipoXSIAtteso = argument.getTypeName();
										}
										argomentoAtteso = RootElementBody.toString(nomeElementAtteso, namespaceElementAtteso, tipoXSIAtteso);
										if(j>0){
											bfMessage.append(", ");
										}
										bfMessage.append(argomentoAtteso);
									}
								}
							}
						}
						bfMessage.append("\n").append(eccezioneActionMatch.toString());

						if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
							throw new WSDLValidatorException("Invalid "+(isRichiesta?"request":"response")+" by WSDL specification '"+uriAccordo+"' (port-type:"+portType+", operation:"+operation+", style:"+style+", use:"+use+"): "+bfMessage.toString());
						}else{
							throw new WSDLValidatorException("Invalid "+(isRichiesta?"request":"response")+" by API specification '"+uriAccordo+"' (service:"+portType+", operation:"+operation+", style:"+style+", use:"+use+"): "+bfMessage.toString());
						}

					}
					else{
						if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
							throw new WSDLValidatorException("Invalid "+(isRichiesta?"request":"response")+" by WSDL specification '"+uriAccordo+"' (port-type:"+portType+", operation:"+operation+", style:"+style+", use:"+use+")");
						}else{
							throw new WSDLValidatorException("Invalid "+(isRichiesta?"request":"response")+" by API specification '"+uriAccordo+"' (service:"+portType+", operation:"+operation+", style:"+style+", use:"+use+")");
						}
					}
				} else {
					if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
						throw new WSDLValidatorException("Operation '"+operation+"' undefined in PortType '"+portType+"' of the WSDL specification '"+uriAccordo+"'");
					}else{
						throw new WSDLValidatorException("Operation '"+operation+"' undefined in Service '"+portType+"' of the API specification '"+uriAccordo+"'");
					}
				}
			}
			
			if( isRichiesta && soapActionWSDL!=null){
				this.logger.debug("CheckSOAPAction");
				String soapActionRipulita = null;
				if(soapAction!=null){
					soapActionRipulita = soapAction.trim();
					if(soapActionRipulita.startsWith("\"")){
						soapActionRipulita = soapActionRipulita.substring(1);
					}
					if(soapActionRipulita.endsWith("\"")){
						soapActionRipulita = soapActionRipulita.substring(0,(soapActionRipulita.length()-1));
					}
				}
				if(soapActionWSDL.equalsIgnoreCase(soapActionRipulita)==false){
					boolean tmpThrowSOAPActionException = throwSOAPActionException;
					if(soapActionRipulita==null && MessageType.SOAP_12.equals(this.messageType)){
						// The SOAP 1.1 mandatory SOAPAction HTTP header has been removed in SOAP 1.2. In its place is an optional action parameter on the application/soap+xml media type.
						// Quindi se nella richiesta non era presente una soapAction, non devo sollevare eccezione
						tmpThrowSOAPActionException = false;
					}
					// Validazione SOAPAction
					if(tmpThrowSOAPActionException){
						if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
							throw new WSDLValidatorException("Invalid soap action '"+soapActionRipulita+"' by WSDL specification '"+uriAccordo+"' (port-type:"+portType+", operation:"+operation+", soap action:"+soapActionWSDL+")");
						}else{
							throw new WSDLValidatorException("Invalid soap action '"+soapActionRipulita+"' by API specification '"+uriAccordo+"' (service:"+portType+", operation:"+operation+", soap action:"+soapActionWSDL+")");
						}
					}
				}
			}
			
			// Controllo sul namespace RPC.
			if(namespaceRPC!=null && rpcElement!=null){
				this.logger.debug("CheckRPCNamespace");
				
				if( (rpcElement.getNamespaceURI()==null || StringUtils.isEmpty(rpcElement.getNamespaceURI())) ){
					if(!this.rpcAcceptRootElementUnqualified) {
						if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
							throw new WSDLValidatorException("Unqualified rpc "+(isRichiesta?"request":"response")+" element '"+rpcElement.getLocalName()+"' by WSDL specification '"+uriAccordo+"' (port-type:"+portType+", operation:"+operation+")");
						}else{
							throw new WSDLValidatorException("Unqualified rpc "+(isRichiesta?"request":"response")+" element '"+rpcElement.getLocalName()+"' by API specification '"+uriAccordo+"' (service:"+portType+", operation:"+operation+")");
						}
					}
				}
				else {
					if(!rpcElement.getNamespaceURI().equals(namespaceRPC)){
						if(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()){
							throw new WSDLValidatorException("Invalid rpc "+(isRichiesta?"request":"response")+" element '"+rpcElement.getLocalName()+"' by WSDL specification '"+uriAccordo+"' (port-type:"+portType+", operation:"+operation+"): expected namespace '"+namespaceRPC+"'; found namespace '"+rpcElement.getNamespaceURI()+"'");
						}else{
							throw new WSDLValidatorException("Invalid rpc "+(isRichiesta?"request":"response")+" element '"+rpcElement.getLocalName()+"' by API specification '"+uriAccordo+"' (service:"+portType+", operation:"+operation+"): expected namespace '"+namespaceRPC+"'; found namespace '"+rpcElement.getNamespaceURI()+"'");
						}
					}
				}
			}
			
		}catch(Exception e){
			String msgErrore = "Validazione WSDL ("+isRichiesta+") fallita: "+e.getMessage();
			if(logErrorAsDebug){
				this.logger.debug(msgErrore);
			}else{
				this.logger.error(msgErrore,e);
			}
			if( (e instanceof WSDLValidatorException)==false )
				this.logger.debug("Validazione WSDL fallita",e);
			// è gia nella definizione dell'error
			StringBuilder errorMsgValidazioneXSDBuilder = new StringBuilder();
			if(this.addPrefixError) {
				if(isRichiesta) {
					errorMsgValidazioneXSDBuilder.append("Request");
				}
				else {
					errorMsgValidazioneXSDBuilder.append("Response");
				}
				errorMsgValidazioneXSDBuilder.append(" content not conform to ").append(this.accordoServizioWrapper.isPortTypesLoadedFromWSDL()?"WSDL":"API").append(" specification; ");
			}
			errorMsgValidazioneXSDBuilder.append(e.getMessage());
			errorMsgValidazioneXSD = errorMsgValidazioneXSDBuilder.toString();
		}
		if(errorMsgValidazioneXSD!=null){
			throw new WSDLValidatorException(errorMsgValidazioneXSD);
		}
		
	}
	
	
	
	
	
	
	
	
	
	/* -------------- VALIDAZIONE UTILITIES --------------------- */
	
	public Element cleanXSITypes(Node node) throws Exception{
		
		//La versione non e' rilevante.
		byte[] element = this.eraserType (this.xmlUtils.toByteArray(node,true));
		Document doc = this.xmlUtils.newDocument(element);
		Element domElem = doc.getDocumentElement();
		
		/*  vecchia impl: 
		MessageElement daClonare = (MessageElement) node;
		//System.out.println("PRIMA ["+daClonare.getAsString()+"] ");
		byte[]element = this.eraserType(daClonare.getAsString().getBytes());
		//System.out.println("DOPO ["+new String(element)+"] ");
		Element e =  XMLUtils.newElement(element);
		*/
		
		return domElem;
	}
	/**
	 * Metodo che si occupa di effettuare l'eliminazione degli xsd:type String
	 *
	 * utility che elimina gli xsd type
	 * @param xml Xml su cui effettuare la pulizia dell'header.
	 * @return byte[] contenente un xml 'pulito'.
	 * 
	 */
	public byte[] eraserType(byte[] xml) throws UtilsException{

		ByteArrayOutputStream cleanXML = null;
		String prefix = "";
		try{
			cleanXML = new ByteArrayOutputStream();

			// Elimino import
			for(int i=0; i<xml.length ; ){

				if(xml[i] == ' '){

					// Cerco Stringa  " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
					String importXSITYPE = "http://www.w3.org/2001/XMLSchema-instance"; 
					if(i+"xmlns:".length() < xml.length){
						if( (xml[i+1] == 'x') &&
							(xml[i+2] == 'm') &&
							(xml[i+3] == 'l') &&
							(xml[i+4] == 'n') &&
							(xml[i+5] == 's') &&
							(xml[i+6] == ':')){
							StringBuilder test = new StringBuilder("xmlns:");
							StringBuilder prefixTest = new StringBuilder();
							int contatoreFineAttributo = 0;
							for(int k=7 ; ; k++){
								if((i+k)>=xml.length)
									break;
								if((char)xml[i+k] == '"')
									contatoreFineAttributo++;
								if(contatoreFineAttributo==0)
									prefixTest.append((char)xml[i+k]);
								test.append((char)xml[i+k]);
								if(contatoreFineAttributo>=2)
									break;
							}
							//System.out.println("TROVATO ["+test.toString()+"] PREFIX["+prefixTest.toString()+"]");
							// Prelevo valore
							int indexFirst = test.toString().indexOf('"');
							int secondFirst = test.toString().indexOf('"', indexFirst+1);
							String testEquals = test.toString().substring(indexFirst+1, secondFirst);
							//System.out.println("TROVATO SUBSTRING ["+testEquals+"]");
							if(importXSITYPE.equalsIgnoreCase(testEquals)){
								//System.out.println("CANCELLO");
								prefix = prefixTest.toString().substring(0, prefixTest.length()-1);
								//System.out.println("CANCELLO P["+prefix+"]");
								// Cancello la stringa trovata
								i = i + test.toString().length() +1;
								continue;
							}
						}
					}
					
					cleanXML.write(xml[i]);
					i++;

				}else{
					cleanXML.write(xml[i]);
					i++;
				}

			}
			byte [] risultato = cleanXML.toByteArray();
			cleanXML = new ByteArrayOutputStream();
			//System.out.println("DOPO step 1 ["+new String(risultato)+"] ");
			cleanXML.close();
			
			// Elimino xsi type
			for(int i=0; i<risultato.length ; ){

				if(risultato[i] == ' '){

					// Cerco Stringa  " xsi:type=\"xsd:TYPE\""
					String XSITYPE_PREFIX = prefix+":type=\""; 
					if(i+XSITYPE_PREFIX.length()+2 < risultato.length){
						StringBuilder test = new StringBuilder("");
						int contatoreFineAttributo = 0;
						for(int k=1 ; ; k++){
							if((i+k)>=risultato.length)
								break;
							if((char)risultato[i+k] == '"')
								contatoreFineAttributo++;
							test.append((char)risultato[i+k]);
							if(contatoreFineAttributo>=2)
								break;
						}
						//stem.out.println("TROVATO ["+test.toString()+"] START WITH["+XSITYPE_PREFIX+"]");
						if(test.toString().startsWith(XSITYPE_PREFIX)){
							// Cancello la stringa trovata
							//System.out.println("CANCELLO");
							i = i + test.toString().length()+1;
							continue;
						}
					}
					
					cleanXML.write(risultato[i]);
					i++;

				}else{
					cleanXML.write(risultato[i]);
					i++;
				}

			}
			risultato = cleanXML.toByteArray();
			cleanXML.close();
			
			return risultato;

		} catch(Exception e) {
			this.logger.error("Utilities.eraserType",e);
			try{
				if(cleanXML!=null)
					cleanXML.close();
			}catch(Exception eis){
				// ignore
			}
			throw new UtilsException("Eliminazione xsi:type per validazione non riuscita "+e.getMessage(),e);
		}
	}  
	
	
	
	
	
	

}

class RootElementBody{
	
	private String localName;
	private String namespace;
	private String xsiType;
	
	// Questo elemento viene valorizzato solo nei casi di wsdl encoded dove gli elementi vengono definiti con wsdl:part element e non type.
	private String namespaceElementoCheContieneXSIType;
		
	public RootElementBody(SOAPEnvelope soapEnvelope,Node nodoPadre,boolean rpc, Node n) throws Exception{
		
		NamedNodeMap attributes = n.getAttributes();
		this.localName = n.getLocalName();
		
		// Prima verifico presenza di xsi:types ...
		if(attributes!=null && attributes.getLength()>0){
			for (int i = 0; i < attributes.getLength(); i++) {
				Node attribute = attributes.item(i);
				if(attribute instanceof Attr){
					Attr a = (Attr) attribute;
					//String attrName = a.getName(); // type
					//String attrPrefix = a.getPrefix(); // xsi
					String attrLocalName = a.getLocalName();
					String attrNamespace = a.getNamespaceURI(); // http://www.w3.org/2001/XMLSchema-instance
					String value = a.getNodeValue(); // messaggioSII:esitoProcessMessaggioSIIType
					//System.out.println("ATTRNAME["+attrName+"] ATTRPREFIX["+attrPrefix+"] ATTRNAMESPACE["+attrNamespace+"] VALUE["+value+"]");
					if(WSDLValidator.XMLSCHEMA_INSTANCE_NAMESPACE.equals(attrNamespace) &&
							WSDLValidator.XMLSCHEMA_INSTANCE_LOCAL_NAME_TYPE.equals(attrLocalName) && 
							value!=null){
						String prefix = "";
						String typeName = value;
						if(value.contains(":")){
							prefix = value.split(":")[0];
							typeName = value.split(":")[1];
						}
						this.xsiType = typeName;
						
						// Cerco namespace corrispondente al prefix.
						// 1. cerco prima nel nodo stesso
						this.namespace = this.mappingPrefixToNamespace(n, prefix);
						if(this.namespace==null){
							if(rpc){
								// 2. cerco nel rpc element
								this.namespace = this.mappingPrefixToNamespace(nodoPadre, prefix);
								if(this.namespace==null){
									// 3. cerco nel soap body
									this.namespace = this.mappingPrefixToNamespace(soapEnvelope.getBody(), prefix);
									if(this.namespace==null){
										// 4. cerco nel soap envelope
										this.namespace = this.mappingPrefixToNamespace(soapEnvelope, prefix);
										if(this.namespace==null){
											throw new Exception("[RPCStyle] Namespace (for prefix "+prefix+") not found for element ["+n.getLocalName()+"] with xsi:type=\""+value+"\"");
										}
									}	
								}
							}else{
								// 2. cerco nel soap body (il nodo padre)
								this.namespace = this.mappingPrefixToNamespace(nodoPadre, prefix);
								if(this.namespace==null){
									// 3. cerco nel soap envelope
									this.namespace = this.mappingPrefixToNamespace(soapEnvelope, prefix);
									if(this.namespace==null){
										throw new Exception("[DocumentStyle] Namespace (for prefix "+prefix+") not found for element ["+n.getLocalName()+"] with xsi:type=\""+value+"\"");
									}
								}	
							}
						}
					}
				}
			}
		}
		
		if(this.xsiType==null){
			this.namespace = n.getNamespaceURI();
		}
		else{
			this.namespaceElementoCheContieneXSIType = n.getNamespaceURI();
		}
	}
	
	private String mappingPrefixToNamespace(Node n,String prefix) {
		NamedNodeMap attributes = n.getAttributes();
		if(attributes==null || attributes.getLength()<=0){
			return null;
		}
		for (int i = 0; i < attributes.getLength(); i++) {
			Node attribute = attributes.item(i);
			if(attribute instanceof Attr){
				Attr a = (Attr) attribute;
				String attrName = a.getName(); // type
				//String attrPrefix = a.getPrefix(); // xsi
				//String attrNamespace = a.getNamespaceURI(); // http://www.w3.org/2001/XMLSchema-instance
				String value = a.getNodeValue(); // messaggioSII:esitoProcessMessaggioSIIType
				//System.out.println("CHECK XMLNS Search["+prefix+"] ATTRNAME["+attrName+"] ATTRPREFIX["+attrPrefix+"] ATTRNAMESPACE["+attrNamespace+"] VALUE["+value+"]");
				
				if(attrName.startsWith("xmlns")){
				
					if(prefix==null || prefix.equals("")){
						if("xmlns".equals(attrName)){
							//System.out.println("FOUND! ["+value+"]");
							return value;
						}
					}
					else{
						if(attrName.equals("xmlns:"+prefix)){
							//System.out.println("FOUND! ["+value+"]");
							return value;
						}
					}
					
				}
				
			}
		}
		return null;
	}
	
	public String getLocalName() {
		return this.localName;
	}
	public void setLocalName(String localName) {
		this.localName = localName;
	}
	public String getNamespace() {
		return this.namespace;
	}
	public void setNamespace(String namespace) {
		this.namespace = namespace;
	}
	public String getXsiType() {
		return this.xsiType;
	}
	public void setXsiType(String xsiType) {
		this.xsiType = xsiType;
	}
	public String getNamespaceElementoCheContieneXSIType() {
		return this.namespaceElementoCheContieneXSIType;
	}
	public void setNamespaceElementoCheContieneXSIType(
			String namespaceElementoCheContieneXSIType) {
		this.namespaceElementoCheContieneXSIType = namespaceElementoCheContieneXSIType;
	}
	
	@Override
	public String toString(){
		return RootElementBody.toString(this.localName, this.namespace, this.xsiType, this.namespaceElementoCheContieneXSIType);
	}
	
	public static String toString(String localName,String namespace,String xsiType){
		return toString(localName, namespace, xsiType, null);
	}
	private static String toString(String localName,String namespace,String xsiType,String namespaceElementoCheContieneXSIType){
		StringBuilder bf = new StringBuilder();
		if(xsiType==null){
			bf.append("{");
			bf.append(namespace);
			bf.append("}");
			bf.append(localName);
		}else{
			bf.append("[xsi:type=\"{");
			bf.append(namespace);
			bf.append("\"}"+xsiType+"]");
			// Questo elemento viene valorizzato solo nei casi di wsdl encoded dove gli elementi vengono definiti con wsdl:part element e non type.
			if(namespaceElementoCheContieneXSIType!=null){
				bf.append("{");
				bf.append(namespaceElementoCheContieneXSIType);
				bf.append("}");
			}
			bf.append(localName);
		}
		return bf.toString();
	}
}