ConnettoreSAAJ.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.pdd.core.connettori;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.soap.MimeHeader;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPMessage;
import org.openspcoop2.core.config.ResponseCachingConfigurazione;
import org.openspcoop2.core.constants.CostantiConnettori;
import org.openspcoop2.core.transazioni.constants.TipoMessaggio;
import org.openspcoop2.message.MessageUtils;
import org.openspcoop2.message.OpenSPCoop2MessageFactory;
import org.openspcoop2.message.OpenSPCoop2SoapMessage;
import org.openspcoop2.message.constants.Costanti;
import org.openspcoop2.message.constants.MessageRole;
import org.openspcoop2.message.constants.MessageType;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.pdd.config.CostantiProprieta;
import org.openspcoop2.pdd.core.Utilities;
import org.openspcoop2.pdd.mdb.ConsegnaContenutiApplicativi;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.date.DateManager;
import org.openspcoop2.utils.io.Base64Utilities;
import org.openspcoop2.utils.io.DumpByteArrayOutputStream;
import org.openspcoop2.utils.resources.Charset;
import org.openspcoop2.utils.transport.TransportResponseContext;
import org.openspcoop2.utils.transport.TransportUtils;
import org.openspcoop2.utils.transport.http.HttpConstants;
import org.openspcoop2.utils.transport.http.HttpRequestMethod;
import org.openspcoop2.utils.transport.http.HttpUtilities;
import org.openspcoop2.utils.transport.http.RFC2047Encoding;
import org.openspcoop2.utils.transport.http.RFC2047Utilities;
/**
* Classe utilizzata per effettuare consegne di messaggi Soap, attraverso
* l'invocazione di un server http.
*
*
* @author Poli Andrea (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class ConnettoreSAAJ extends ConnettoreBaseWithResponse {
@Override
public String getProtocollo() {
return "HTTP";
}
public static final String ENDPOINT_TYPE = "saaj";
/** Connessione */
private SOAPConnection connection = null;
private SOAPMessage soapRequestMessage = null;
/* ******** METODI ******** */
@Override
protected boolean initializePreSend(ResponseCachingConfigurazione responseCachingConfig, ConnettoreMsg request) {
if(this.initialize(request, true, responseCachingConfig)==false){
return false;
}
return true;
}
@Override
protected boolean send(ConnettoreMsg request) {
// analsi i parametri specifici per il connettore
if(this.properties.get(CostantiConnettori.CONNETTORE_LOCATION)==null){
this.errore = "Proprieta' '"+CostantiConnettori.CONNETTORE_LOCATION+"' non fornita e richiesta da questo tipo di connettore ["+request.getTipoConnettore()+"]";
return false;
}
return sendSAAJ();
}
/**
* Si occupa di effettuare la consegna SOAP (invocazione di un WebService).
* Si aspetta di ricevere una risposta non sbustata.
*
* @return true in caso di consegna con successo, false altrimenti
*
*/
private boolean sendSAAJ(){
int connectionTimeout = -1;
boolean connectionTimeoutConfigurazioneGlobale = true;
int readConnectionTimeout = -1;
boolean readConnectionTimeoutConfigurazioneGlobale = true;
try{
// Impostazione timeout
if(this.debug)
this.logger.debug("Impostazione timeout...");
if(this.properties.get(CostantiConnettori.CONNETTORE_CONNECTION_TIMEOUT)!=null){
try{
connectionTimeout = Integer.parseInt(this.properties.get(CostantiConnettori.CONNETTORE_CONNECTION_TIMEOUT));
connectionTimeoutConfigurazioneGlobale = this.properties.containsKey(CostantiConnettori.CONNETTORE_CONNECTION_TIMEOUT_GLOBALE);
}catch(Exception e){
this.logger.error("Parametro '"+CostantiConnettori.CONNETTORE_CONNECTION_TIMEOUT+"' errato",e);
}
}
if(connectionTimeout==-1){
connectionTimeout = HttpUtilities.HTTP_CONNECTION_TIMEOUT;
}
if(this.properties.get(CostantiConnettori.CONNETTORE_READ_CONNECTION_TIMEOUT)!=null){
try{
readConnectionTimeout = Integer.parseInt(this.properties.get(CostantiConnettori.CONNETTORE_READ_CONNECTION_TIMEOUT));
readConnectionTimeoutConfigurazioneGlobale = this.properties.containsKey(CostantiConnettori.CONNETTORE_READ_CONNECTION_TIMEOUT_GLOBALE);
}catch(Exception e){
this.logger.error("Parametro '"+CostantiConnettori.CONNETTORE_READ_CONNECTION_TIMEOUT+"' errato",e);
}
}
if(readConnectionTimeout==-1){
readConnectionTimeout = HttpUtilities.HTTP_READ_CONNECTION_TIMEOUT;
}
if(this.debug)
this.logger.info("Impostazione http timeout CT["+connectionTimeout+"] RT["+readConnectionTimeout+"]...",false);
// check validity message
if(ServiceBinding.SOAP.equals(this.requestMsg.getServiceBinding())==false) {
throw new Exception("Connettore utilizzabile solamente in un contesto SOAP (trovato: "+this.requestMsg.getServiceBinding()+")");
}
// check validita URL
if(this.debug)
this.logger.debug("Check validita URL...");
this.location = this.properties.get(CostantiConnettori.CONNETTORE_LOCATION);
this.location = ConnettoreUtils.buildLocationWithURLBasedParameter(this.logger!=null ? this.logger.getLogger() : null, this.requestMsg, ENDPOINT_TYPE, this.propertiesUrlBased, this.location,
this.getProtocolFactory(), this.idModulo);
URL urlTest = new URL( this.location );
URLConnection connectionTest = urlTest.openConnection();
HttpURLConnection httpConnTest = (HttpURLConnection) connectionTest;
httpConnTest.setRequestMethod( HttpRequestMethod.POST.name() );
httpConnTest.setConnectTimeout(connectionTimeout);
httpConnTest.setReadTimeout(readConnectionTimeout);
httpConnTest.setDoOutput(true);
java.io.OutputStream outTest = httpConnTest.getOutputStream();
outTest.close();
httpConnTest.disconnect();
// Check tipo di messaggio
MessageType requestMessageType = this.requestMsg.getMessageType();
if(ServiceBinding.SOAP.equals(this.requestMsg.getServiceBinding())==false){
throw new Exception("Connettore utilizzabile solamente per tipologia di servizio SOAP");
}
OpenSPCoop2SoapMessage soapRequestMessage = this.requestMsg.castAsSoap();
this.soapRequestMessage = MessageUtils.getSOAPMessage(soapRequestMessage, false, this.idTransazione);
// Collezione header di trasporto per dump
Map<String, List<String>> propertiesTrasportoDebug = null;
if(this.isDumpBinarioRichiesta()) {
propertiesTrasportoDebug = new HashMap<>();
}
// impostazione property
if(this.debug)
this.logger.debug("PrefixOptimization...");
String prefixOptimization = "true";
if(this.properties.get("prefix-optimization")!=null){
if("false".equalsIgnoreCase(this.properties.get("prefix-optimization")) )
prefixOptimization = "false";
this.logger.info("Prefix Optimization = '"+prefixOptimization+"'",false);
//AxisProperties.setProperty(AxisEngine.PROP_ENABLE_NAMESPACE_PREFIX_OPTIMIZATION,prefixOptimization);
}
// Consegna
if(this.debug)
this.logger.debug("Creazione connessione...");
OpenSPCoop2MessageFactory messageFactory = Utilities.getOpenspcoop2MessageFactory(this.logger.getLogger(),this.requestMsg, this.requestInfo, MessageRole.NONE);
SOAPConnectionFactory soapConnFactory = messageFactory.getSOAPConnectionFactory();
this.connection = soapConnFactory.createConnection();
try{
// Impostazione Proprieta del trasporto
// Impostazione Content-Type
String contentTypeRichiesta = null;
if(this.debug)
this.logger.debug("Impostazione content type...");
if(this.isSoap){
contentTypeRichiesta = this.requestMsg.getContentType();
if(contentTypeRichiesta==null){
throw new Exception("Content-Type del messaggio da spedire non definito");
}
}
else{
contentTypeRichiesta = this.requestMsg.getContentType();
// Content-Type non obbligatorio in REST
}
if(this.debug)
this.logger.info("Impostazione Content-Type ["+contentTypeRichiesta+"]",false);
if(contentTypeRichiesta!=null){
setRequestHeader(HttpConstants.CONTENT_TYPE, contentTypeRichiesta, propertiesTrasportoDebug);
}
// Authentication BASIC
if(this.debug)
this.logger.debug("Impostazione autenticazione...");
String user = null;
String password = null;
if(this.credenziali!=null){
user = this.credenziali.getUser();
password = this.credenziali.getPassword();
}else{
user = this.properties.get(CostantiConnettori.CONNETTORE_USERNAME);
password = this.properties.get(CostantiConnettori.CONNETTORE_PASSWORD);
}
if(user!=null && password!=null){
String authentication = user + ":" + password;
authentication = HttpConstants.AUTHORIZATION_PREFIX_BASIC + Base64Utilities.encodeAsString(authentication.getBytes());
setRequestHeader(HttpConstants.AUTHORIZATION,authentication, propertiesTrasportoDebug);
if(this.debug)
this.logger.info("Impostazione autenticazione (username:"+user+" password:"+password+") ["+authentication+"]",false);
}
// Impostazione timeout
if(this.debug)
this.logger.debug("Set timeout...");
//TODO: connection.setTimeout(readConnectionTimeout);
// Impostazione Proprieta del trasporto
if(this.debug)
this.logger.debug("Impostazione header di trasporto...");
boolean encodingRFC2047 = false;
Charset charsetRFC2047 = null;
RFC2047Encoding encodingAlgorithmRFC2047 = null;
boolean validazioneHeaderRFC2047 = false;
if(this.idModulo!=null){
if(ConsegnaContenutiApplicativi.ID_MODULO.equals(this.idModulo)){
encodingRFC2047 = this.openspcoopProperties.isEnabledEncodingRFC2047HeaderValueConsegnaContenutiApplicativi();
charsetRFC2047 = this.openspcoopProperties.getCharsetEncodingRFC2047HeaderValueConsegnaContenutiApplicativi();
encodingAlgorithmRFC2047 = this.openspcoopProperties.getEncodingRFC2047HeaderValueConsegnaContenutiApplicativi();
validazioneHeaderRFC2047 = this.openspcoopProperties.isEnabledValidazioneRFC2047HeaderNameValueConsegnaContenutiApplicativi();
}else{
encodingRFC2047 = this.openspcoopProperties.isEnabledEncodingRFC2047HeaderValueInoltroBuste();
charsetRFC2047 = this.openspcoopProperties.getCharsetEncodingRFC2047HeaderValueInoltroBuste();
encodingAlgorithmRFC2047 = this.openspcoopProperties.getEncodingRFC2047HeaderValueInoltroBuste();
validazioneHeaderRFC2047 = this.openspcoopProperties.isEnabledValidazioneRFC2047HeaderNameValueInoltroBuste();
}
encodingRFC2047 = CostantiProprieta.isConnettoriHeaderValueEncodingRFC2047RequestEnabled(this.proprietaPorta, encodingRFC2047);
charsetRFC2047 = CostantiProprieta.getConnettoriHeaderValueEncodingRFC2047RequestCharset(this.proprietaPorta, charsetRFC2047);
encodingAlgorithmRFC2047 = CostantiProprieta.getConnettoriHeaderValueEncodingRFC2047RequestType(this.proprietaPorta, encodingAlgorithmRFC2047);
validazioneHeaderRFC2047 = CostantiProprieta.isConnettoriHeaderValidationRequestEnabled(this.proprietaPorta, validazioneHeaderRFC2047);
}
this.forwardHttpRequestHeader();
if(this.propertiesTrasporto != null){
Iterator<String> keys = this.propertiesTrasporto.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
if(HttpConstants.USER_AGENT.equalsIgnoreCase(key)){
continue;
}
List<String> values = this.propertiesTrasporto.get(key);
if(this.debug) {
if(values!=null && !values.isEmpty()) {
for (String value : values) {
this.logger.info("Set Transport Header ["+key+"]=["+value+"]",false);
}
}
}
if(encodingRFC2047){
List<String> valuesEncoded = new ArrayList<>();
if(values!=null && !values.isEmpty()) {
for (String value : values) {
if(RFC2047Utilities.isAllCharactersInCharset(value, charsetRFC2047)==false){
String encoded = RFC2047Utilities.encode(new String(value), charsetRFC2047, encodingAlgorithmRFC2047);
//System.out.println("@@@@ CODIFICA ["+value+"] in ["+encoded+"]");
if(this.debug)
this.logger.info("RFC2047 Encoded value in ["+encoded+"] (charset:"+charsetRFC2047+" encoding-algorithm:"+encodingAlgorithmRFC2047+")",false);
valuesEncoded.add(encoded);
}
else{
valuesEncoded.add(value);
}
}
}
setRequestHeader(validazioneHeaderRFC2047, key, valuesEncoded, this.logger, propertiesTrasportoDebug);
}
else{
setRequestHeader(validazioneHeaderRFC2047, key, values, this.logger, propertiesTrasportoDebug);
}
}
}
// Aggiunta del SoapAction Header in caso di richiesta SOAP
// spostato sotto il forwardHeader per consentire alle trasformazioni di modificarla
if(this.isSoap && this.sbustamentoSoap == false){
if(this.debug)
this.logger.debug("Impostazione soap action...");
boolean existsTransportProperties = false;
if(TransportUtils.containsKey(this.propertiesTrasporto, Costanti.SOAP11_MANDATORY_HEADER_HTTP_SOAP_ACTION)){
this.soapAction = TransportUtils.getFirstValue(this.propertiesTrasporto, Costanti.SOAP11_MANDATORY_HEADER_HTTP_SOAP_ACTION);
existsTransportProperties = (this.soapAction!=null);
}
if(!existsTransportProperties) {
this.soapAction = soapRequestMessage.getSoapAction();
}
if(this.soapAction==null){
this.soapAction="\"OpenSPCoop\"";
}
if(MessageType.SOAP_11.equals(this.requestMsg.getMessageType()) && !existsTransportProperties){
// NOTA non quotare la soap action, per mantenere la trasparenza della PdD
setRequestHeader(Costanti.SOAP11_MANDATORY_HEADER_HTTP_SOAP_ACTION,this.soapAction, propertiesTrasportoDebug);
}
if(this.debug)
this.logger.info("SOAP Action inviata ["+this.soapAction+"]",false);
}
if(this.isDumpBinarioRichiesta()) {
DumpByteArrayOutputStream bout = new DumpByteArrayOutputStream(this.dumpBinario_soglia, this.dumpBinario_repositoryFile, this.idTransazione,
TipoMessaggio.RICHIESTA_USCITA_DUMP_BINARIO.getValue());
try {
this.emitDiagnosticStartDumpBinarioRichiestaUscita();
this.requestMsg.writeTo(bout, false);
bout.flush();
bout.close();
this.dumpBinarioRichiestaUscita(bout, requestMessageType, contentTypeRichiesta, this.location, propertiesTrasportoDebug);
}finally {
try {
bout.clearResources();
}catch(Throwable t) {
this.logger.error("Release resources failed: "+t.getMessage(),t);
}
}
}
// Send
if(this.debug)
this.logger.debug("Send...");
SOAPMessage soapMsg = this.connection.call( this.soapRequestMessage, this.location);
this.dataRichiestaInoltrata = DateManager.getDate();
this.responseMsg = messageFactory.
createMessage(this.requestMsg.getMessageType(),MessageRole.RESPONSE,soapMsg);
this.dataAccettazioneRisposta = DateManager.getDate();
}catch(javax.xml.soap.SOAPException sendError){
this.eccezioneProcessamento = sendError;
String errorMsg = this.readExceptionMessageFromException(sendError);
this.connection.close();
this.errore = "Errore avvenuto durante la consegna SOAP (lettura risposta): " + errorMsg;
return false;
}
// check consistenza risposta
if(this.debug)
this.logger.debug("Check consistenza...");
SOAPMessage soapResponse = null;
if(this.responseMsg!=null){
OpenSPCoop2SoapMessage soapMessageResponse = this.responseMsg.castAsSoap();
soapResponse = soapMessageResponse.getSOAPMessage();
if(soapMessageResponse.getSOAPBody()!=null){
if(soapMessageResponse.hasSOAPFault() ){
if(soapMessageResponse.getSOAPBody().getFault().getFaultString().indexOf("Premature end of file") != -1){
// L'errore 'premature end of file' consiste solo nel fatto che una risposta non e' stata ricevuta.
this.responseMsg = null;
}
}
}
}
if(soapResponse!=null){
// Analisi MimeType e ContentLocation della risposta
if(soapResponse.getMimeHeaders()!=null){
if(this.propertiesTrasportoRisposta==null){
this.propertiesTrasportoRisposta = new HashMap<>();
}
Iterator<?> it = soapResponse.getMimeHeaders().getAllHeaders();
while (it.hasNext()) {
MimeHeader mh = (MimeHeader) it.next();
if(mh!=null && mh.getName()!=null && mh.getValue()!=null) {
TransportUtils.addHeader(this.propertiesTrasportoRisposta,mh.getName(),mh.getValue());
if("content-length".equalsIgnoreCase(mh.getName())){
try{
this.contentLength = Integer.parseInt(mh.getValue());
}catch(Exception e){}
}
if("content-type".equalsIgnoreCase(mh.getName())){
try{
this.tipoRisposta = mh.getValue();
}catch(Exception e){}
}
}
}
}
}
// return code
this.codice = 200;
// content length
if(this.responseMsg!=null && this.contentLength<0){
this.contentLength = this.responseMsg.getIncomingMessageContentLength();
}
if(this.responseMsg!=null){
TransportResponseContext responseContext = new TransportResponseContext(this.logger.getLogger());
responseContext.setCodiceTrasporto(this.codice+"");
responseContext.setContentLength(this.contentLength);
responseContext.setHeaders(this.propertiesTrasportoRisposta);
this.responseMsg.setTransportResponseContext(responseContext);
this.messageTypeResponse = this.requestMsg.getMessageType();
if(this.tipoRisposta==null) {
this.tipoRisposta = this.requestMsg.getContentType();
}
}
if(this.isDumpBinarioRisposta()){
if(this.responseMsg!=null){
// Registro Debug.
DumpByteArrayOutputStream bout = null;
try {
try {
bout = new DumpByteArrayOutputStream(this.dumpBinario_soglia, this.dumpBinario_repositoryFile, this.idTransazione,
TipoMessaggio.RISPOSTA_INGRESSO_DUMP_BINARIO.getValue());
this.emitDiagnosticStartDumpBinarioRispostaIngresso();
this.responseMsg.writeTo(bout, false);
}finally {
try {
if(bout!=null) {
bout.flush();
}
}catch(Throwable t) {
// ignore
}
try {
if(bout!=null) {
bout.close();
}
}catch(Throwable t) {
// ignore
}
}
if(this.debug) {
this.logger.info("Messaggio ricevuto (ContentType:"+this.tipoRisposta+") :\n"+bout.toString(),false);
}
this.dumpBinarioRispostaIngresso(bout, this.messageTypeResponse, this.propertiesTrasportoRisposta);
if(this.contentLength<0){
this.contentLength=bout.size(); // la prendo dal dump, poiche' in saaj non ho modo di agganciare il counting stream
}
}finally {
try {
if(bout!=null) {
bout.clearResources();
}
}catch(Throwable t) {
this.logger.error("Release resources failed: "+t.getMessage(),t);
}
}
}
else {
if(this.debug) {
if(this.tipoRisposta!=null) {
this.logger.info("Messaggio ricevuto (ContentType:"+this.tipoRisposta+") senza contenuto nell'http-reply",false);
}
else {
this.logger.info("Messaggio ricevuto senza contenuto nell'http-reply",false);
}
}
// devo registrare almeno gli header HTTP
this.emitDiagnosticStartDumpBinarioRispostaIngresso();
this.dumpBinarioRispostaIngresso(null, null, this.propertiesTrasportoRisposta);
}
}
if(this.debug)
this.logger.info("Gestione invio/risposta http effettuata con successo",false);
/* ------------ PostOutRequestHandler ------------- */
this.postOutRequest();
// /* ------------ PreInResponseHandler ------------- */
// Con connettore SAAJ non e' utilizzabile
// this.preInResponse();
//
// // Lettura risposta parametri NotifierInputStream per la risposta
// org.openspcoop.utils.io.notifier.NotifierInputStreamParams notifierInputStreamParams = null;
// if(this.preInResponseContext!=null){
// notifierInputStreamParams = this.preInResponseContext.getNotifierInputStreamParams();
// }
return true;
} catch(Exception e){
this.eccezioneProcessamento = e;
String msgErrore = this.readExceptionMessageFromException(e);
if(this.generateErrorWithConnectorPrefix) {
this.errore = "Errore avvenuto durante la consegna SOAP: "+msgErrore;
}
else {
this.errore = msgErrore;
}
this.logger.error("Errore avvenuto durante la consegna SOAP: "+msgErrore,e);
this.processConnectionTimeoutException(connectionTimeout, connectionTimeoutConfigurazioneGlobale, e, msgErrore);
this.processReadTimeoutException(readConnectionTimeout, readConnectionTimeoutConfigurazioneGlobale, e, msgErrore);
return false;
}
}
/**
* Effettua la disconnessione
*/
@Override
public void disconnect() throws ConnettoreException{
List<Throwable> listExceptionChiusura = new ArrayList<Throwable>();
try{
if(this.connection!=null){
if(this.debug && this.logger!=null)
this.logger.debug("Connection.close ...");
try {
this.connection.close();
}catch(Throwable t) {
if(this.logger!=null) {
this.logger.debug("Chiusura socket fallita: "+t.getMessage(),t);
}
listExceptionChiusura.add(t);
}
}
// super.disconnect (Per risorse base)
try {
super.disconnect();
}catch(Throwable t) {
if(this.logger!=null) {
this.logger.debug("Chiusura risorse fallita: "+t.getMessage(),t);
}
listExceptionChiusura.add(t);
}
}catch(Exception e){
throw new ConnettoreException("Chiusura connessione non riuscita: "+e.getMessage(),e);
}
if(listExceptionChiusura!=null && !listExceptionChiusura.isEmpty()) {
org.openspcoop2.utils.UtilsMultiException multiException = new org.openspcoop2.utils.UtilsMultiException(listExceptionChiusura.toArray(new Throwable[1]));
throw new ConnettoreException("Chiusura connessione non riuscita: "+multiException.getMessage(),multiException);
}
}
private void setRequestHeader(boolean validazioneHeaderRFC2047, String key, List<String> values, ConnettoreLogger logger, Map<String, List<String>> propertiesTrasportoDebug) throws Exception {
if(validazioneHeaderRFC2047){
try{
RFC2047Utilities.validHeader(key, values);
setRequestHeader(key, values, propertiesTrasportoDebug);
}catch(UtilsException e){
logger.error(e.getMessage(),e);
}
}
else{
setRequestHeader(key,values, propertiesTrasportoDebug);
}
}
@Override
protected void setRequestHeader(String key,List<String> values) throws Exception {
if(values!=null && !values.isEmpty()) {
for (String value : values) {
this.soapRequestMessage.getMimeHeaders().addHeader(key,value);
}
}
}
}