ConnectorDispatcherUtils.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.services.connector;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.message.exception.ParseException;
import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
import org.openspcoop2.pdd.core.CostantiPdD;
import org.openspcoop2.pdd.core.integrazione.HeaderIntegrazione;
import org.openspcoop2.pdd.core.integrazione.UtilitiesIntegrazione;
import org.openspcoop2.pdd.services.connector.messages.ConnectorOutMessage;
import org.openspcoop2.pdd.services.error.RicezioneBusteExternalErrorGenerator;
import org.openspcoop2.pdd.services.error.RicezioneContenutiApplicativiInternalErrorGenerator;
import org.openspcoop2.protocol.sdk.IProtocolFactory;
import org.openspcoop2.protocol.sdk.constants.ErroreIntegrazione;
import org.openspcoop2.protocol.sdk.constants.ErroriIntegrazione;
import org.openspcoop2.protocol.sdk.constants.IDService;
import org.openspcoop2.protocol.sdk.constants.IntegrationFunctionError;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.utils.CopyStream;
import org.openspcoop2.utils.transport.TransportUtils;
import org.openspcoop2.utils.transport.http.HttpConstants;
import org.openspcoop2.utils.transport.http.HttpRequestMethod;
import org.slf4j.Logger;
import org.w3c.dom.Document;

/**
 * RicezioneContenutiApplicativiIntegrationManagerService
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class ConnectorDispatcherUtils {

	public static final boolean CLIENT_ERROR = true;
	public static final boolean GENERAL_ERROR = false;
	
	public static void doServiceBindingNotSupported(HttpServletRequest req, HttpServletResponse res, HttpRequestMethod httpMethod, ServiceBinding serviceBinding, IDService idService) throws IOException{
		
		OpenSPCoop2Properties op2Properties = OpenSPCoop2Properties.getInstance();
		
		// messaggio di errore
		boolean errore404 = false;
		if(op2Properties!=null){
			if(IDService.PORTA_DELEGATA.equals(idService)){
				if(op2Properties.isGenerazioneErroreHttpMethodUnsupportedPortaDelegataEnabled()==false){
					errore404 = true;
				}
			}
			else if(IDService.PORTA_DELEGATA_XML_TO_SOAP.equals(idService)){
				if(op2Properties.isGenerazioneErroreHttpMethodUnsupportedPortaDelegataImbustamentoSOAPEnabled()==false){
					errore404 = true;
				}
			}
			else if(IDService.PORTA_APPLICATIVA.equals(idService)){
				if(op2Properties.isGenerazioneErroreHttpMethodUnsupportedPortaDelegataEnabled()==false){
					errore404 = true;
				}
			}
			else{
				throw new IOException("Service ["+idService+"] not supported");
			}
		}
		
		if(errore404){
			res.sendError(404,ConnectorUtils.generateError404Message(ConnectorUtils.getFullCodeServiceBindingNotSupported(idService, serviceBinding)));
		}
		else{
		
			res.setStatus(500);
			
			ConnectorUtils.generateErrorMessage(idService,httpMethod,req,res, ConnectorUtils.getMessageServiceBindingNotSupported(serviceBinding), false, true);
			
			try{
				res.getOutputStream().flush();
			}catch(Exception eClose){
				// ignore
			}
			try{
				res.getOutputStream().close();
			}catch(Exception eClose){
				// ignore
			}
			
		}
	}
	
	public static void doMethodNotSupported(HttpServletRequest req, HttpServletResponse res, HttpRequestMethod method, IDService idService) throws IOException{
		
		OpenSPCoop2Properties op2Properties = OpenSPCoop2Properties.getInstance();
		
		// messaggio di errore
		boolean errore404 = false;
		if(op2Properties!=null){
			if(IDService.PORTA_DELEGATA.equals(idService)){
				if(op2Properties.isGenerazioneErroreHttpMethodUnsupportedPortaDelegataEnabled()==false){
					errore404 = true;
				}
			}
			else if(IDService.PORTA_DELEGATA_XML_TO_SOAP.equals(idService)){
				if(op2Properties.isGenerazioneErroreHttpMethodUnsupportedPortaDelegataImbustamentoSOAPEnabled()==false){
					errore404 = true;
				}
			}
			else if(IDService.PORTA_APPLICATIVA.equals(idService)){
				if(op2Properties.isGenerazioneErroreHttpMethodUnsupportedPortaDelegataEnabled()==false){
					errore404 = true;
				}
			}
			else{
				throw new IOException("Service ["+idService+"] not supported");
			}
		}
		
		if(errore404){
			res.sendError(404,ConnectorUtils.generateError404Message(ConnectorUtils.getFullCodeHttpMethodNotSupported(idService, method)));
		}
		else{
		
			res.setStatus(500);
			
			ConnectorUtils.generateErrorMessage(idService,method,req,res, ConnectorUtils.getMessageHttpMethodNotSupported(method), false, true);
			
			try{
				res.getOutputStream().flush();
			}catch(Exception eClose){
				// ignore
			}
			try{
				res.getOutputStream().close();
			}catch(Exception eClose){
				// ignore
			}
			
		}
	}
	
	
	public static void doWsdl(HttpServletRequest req, HttpServletResponse res, HttpRequestMethod method, IDService idService ) throws IOException{
		
		OpenSPCoop2Properties op2Properties = OpenSPCoop2Properties.getInstance();
		String versione = CostantiPdD.OPENSPCOOP2_PRODUCT_VERSION;
		if(op2Properties!=null){
			versione = op2Properties.getPddDetailsForServices();
		}
		
		
		InputStream is =null;
		try{
			
			String wsdl = null;
			boolean generazioneWsdlEnabled = false;
			if(IDService.PORTA_DELEGATA.equals(idService)){
				wsdl = "/PD.wsdl";
				if(op2Properties!=null){
					generazioneWsdlEnabled = op2Properties.isGenerazioneWsdlPortaDelegataEnabled();
				}
			}
			else if(IDService.PORTA_APPLICATIVA.equals(idService)){
				wsdl = "/PA.wsdl";
				if(op2Properties!=null){
					generazioneWsdlEnabled = op2Properties.isGenerazioneWsdlPortaApplicativaEnabled();
				}
			}
			else{
				throw new Exception("Service ["+idService+"] not supported");
			}
			
			is = RicezioneContenutiApplicativiConnector.class.getResourceAsStream(wsdl);
			ByteArrayOutputStream bout = new ByteArrayOutputStream();

			if(is!=null){
//				int letti = 0;
//				byte [] buffer = new byte[Utilities.DIMENSIONE_BUFFER];
//				while( (letti=is.read(buffer)) != -1 ){
//					bout.write(buffer, 0, letti);
//				}
				CopyStream.copy(is, bout);
				bout.flush();
				bout.close();
			}
			
			if(generazioneWsdlEnabled){
			
				if(bout.size()<=0){
					throw new Exception("WSDL Not Found");
				}
				
				byte[] b = bout.toByteArray();
				org.openspcoop2.message.xml.MessageXMLUtils xmlUtils = org.openspcoop2.message.xml.MessageXMLUtils.DEFAULT;
				Document d = xmlUtils.newDocument(b);
				d.getFirstChild().appendChild(d.createComment(versione));
				xmlUtils.writeTo(d, res.getOutputStream());
				
			}
			else{
				
				res.sendError(404, ConnectorUtils.generateError404Message(ConnectorUtils.getFullCodeWsdlUnsupported(idService)));
				
			}
			
		}catch(Exception e){					
			res.setStatus(500);
			
			ConnectorUtils.generateErrorMessage(idService,method,req,res, "Generazione WSDL non riuscita", false, true);
			
			ConnectorUtils.getErrorLog().error("Generazione WSDL "+idService+" non riuscita",e);	
		}finally{
			try{
				res.getOutputStream().flush();
			}catch(Exception eClose){
				// ignore
			}
			try{
				res.getOutputStream().close();
			}catch(Exception eClose){
				// ignore
			}
			try{
				if(is!=null)
					is.close();
			}catch(Exception eClose){
				// ignore
			}
		}
		return;
	}
	
	public static void doError(RequestInfo requestInfo,
			RicezioneContenutiApplicativiInternalErrorGenerator generatoreErrore, ErroreIntegrazione erroreIntegrazione,
			IntegrationFunctionError integrationFunctionError, Throwable e, HttpServletResponse res, Logger log){
		
		IProtocolFactory<?> protocolFactory = requestInfo.getProtocolFactory();
				
		if(generatoreErrore!=null){
			OpenSPCoop2Message msgErrore = generatoreErrore.build(null, integrationFunctionError, erroreIntegrazione, e, null);
			if(msgErrore.getForcedResponseCode()!=null){
				res.setStatus(Integer.parseInt(msgErrore.getForcedResponseCode()));
			}
			try{
				msgErrore.writeTo(res.getOutputStream(), true);
			}catch(Exception eWriteTo){
				log.error("Errore durante la serializzazione dell'errore: "+e.getMessage(),e);
				try{
					res.sendError(500);
				}catch(Exception eInternal){
					throw new RuntimeException(eInternal.getMessage(),eInternal);
				}
			}
		}
		else{
			try{
				res.sendError(500,erroreIntegrazione.getDescrizione(protocolFactory));
			}catch(Exception eInternal){
				throw new RuntimeException(eInternal.getMessage(),eInternal);
			}
		}
		
	}
	
	public static ConnectorDispatcherErrorInfo doError(RequestInfo requestInfo,
			RicezioneContenutiApplicativiInternalErrorGenerator generatoreErrore, ErroreIntegrazione erroreIntegrazione,
			IntegrationFunctionError integrationFunctionError, Throwable e, ParseException parseException,
			ConnectorOutMessage res, Logger log, boolean clientError) throws ConnectorException{
		
		IProtocolFactory<?> protocolFactory = requestInfo.getProtocolFactory();
		
		if(generatoreErrore!=null){
			
			OpenSPCoop2Message msgErrore = generatoreErrore.build(null, integrationFunctionError, erroreIntegrazione, e, parseException);
			
			return _doError(requestInfo, res, log, true, erroreIntegrazione, integrationFunctionError, e, parseException, msgErrore, clientError);
			
		}
		else{
			try{
				String errore = erroreIntegrazione.getDescrizione(protocolFactory);
				throw new ConnectorException(errore);
			}catch(Throwable eInternal){
				throw new ConnectorException(eInternal.getMessage(),eInternal);
			}
		}
		
	}
	
	public static void doError(RequestInfo requestInfo,
			RicezioneBusteExternalErrorGenerator generatoreErrore, ErroreIntegrazione erroreIntegrazione,
			IntegrationFunctionError integrationFunctionError, Throwable e, HttpServletResponse res, Logger log){
		
		IProtocolFactory<?> protocolFactory = requestInfo.getProtocolFactory();
				
		if(generatoreErrore!=null){
			OpenSPCoop2Message msgErrore = generatoreErrore.buildErroreProcessamento(null, integrationFunctionError, erroreIntegrazione, e);
			if(msgErrore.getForcedResponseCode()!=null){
				res.setStatus(Integer.parseInt(msgErrore.getForcedResponseCode()));
			}
			try{
				msgErrore.writeTo(res.getOutputStream(), true);
			}catch(Exception eWriteTo){
				log.error("Errore durante la serializzazione dell'errore: "+e.getMessage(),e);
				try{
					res.sendError(500);
				}catch(Exception eInternal){
					throw new RuntimeException(eInternal.getMessage(),eInternal);
				}
			}
		}
		else{
			try{
				res.sendError(500,erroreIntegrazione.getDescrizione(protocolFactory));
			}catch(Exception eInternal){
				throw new RuntimeException(eInternal.getMessage(),eInternal);
			}
		}
		
	}
	
	public static ConnectorDispatcherErrorInfo doError(RequestInfo requestInfo,
			RicezioneBusteExternalErrorGenerator generatoreErrore, ErroreIntegrazione erroreIntegrazione,
			IntegrationFunctionError integrationFunctionError, Throwable e, ParseException parseException,
			ConnectorOutMessage res, Logger log, boolean clientError) throws ConnectorException{
		
		IProtocolFactory<?> protocolFactory = requestInfo.getProtocolFactory();
				
		if(generatoreErrore!=null){
		
			OpenSPCoop2Message msgErrore = generatoreErrore.buildErroreProcessamento(null, integrationFunctionError, erroreIntegrazione, e);
			
			return _doError(requestInfo, res, log, false, erroreIntegrazione, integrationFunctionError, e, parseException, msgErrore, clientError);
			
		}
		else{
			try{
				String errore = erroreIntegrazione.getDescrizione(protocolFactory);
				throw new ConnectorException(errore);
			}catch(Throwable eInternal){
				throw new ConnectorException(eInternal.getMessage(),eInternal);
			}
		}
		
	}
	
	
	
	private static ConnectorDispatcherErrorInfo _doError(RequestInfo requestInfo,ConnectorOutMessage res,Logger log,boolean portaDelegata,
			ErroreIntegrazione erroreIntegrazione,
			IntegrationFunctionError integrationFunctionError, Throwable e, ParseException parseException,
			OpenSPCoop2Message msg, boolean clientError) throws ConnectorException{
		
		if(requestInfo==null) {
			throw new ConnectorException("RequestInfo param is null");
		}
		
		IProtocolFactory<?> protocolFactory = requestInfo.getProtocolFactory();
		
		Map<String, List<String>> trasporto = new HashMap<>();
		try {
			UtilitiesIntegrazione utilitiesIntegrazione = null;
			if(portaDelegata) {
				utilitiesIntegrazione = UtilitiesIntegrazione.getInstancePDResponse(log);
			}
			else {
				utilitiesIntegrazione = UtilitiesIntegrazione.getInstancePAResponse(log);
			}
			if(requestInfo!=null && requestInfo.getIdTransazione()!=null) {
				HeaderIntegrazione hdr = new HeaderIntegrazione(requestInfo.getIdTransazione());
				utilitiesIntegrazione.setTransportProperties(hdr,trasporto,null);
			}
			else {
				utilitiesIntegrazione.setInfoProductTransportProperties(trasporto);
			}
			utilitiesIntegrazione.setSecurityHeaders(msg!=null ? msg.getServiceBinding() : ServiceBinding.REST, requestInfo, trasporto, null);
			if(trasporto.size()>0){
				Iterator<String> keys = trasporto.keySet().iterator();
				while (keys.hasNext()) {
					String key = (String) keys.next();
					List<String> values = trasporto.get(key);
					if(values!=null && !values.isEmpty()) {
						for (String value : values) {
				    		res.addHeader(key,value);
						}
					}
		    	}	
			}
			
		}catch(Throwable error){
			log.error("Errore durante la serializzazione degli headers: "+error.getMessage(),error);
			try{
				throw new ConnectorException(erroreIntegrazione.getDescrizione(protocolFactory),e);
			}catch(Throwable eInternal){
				// rilancio eccezione originale
				throw new ConnectorException(e.getMessage(),e);
			}
		}
		
		int status = 200;
		String contentTypeRisposta = null;
		if(msg!=null && msg.getForcedResponseCode()!=null){
			status = Integer.parseInt(msg.getForcedResponseCode());
			res.setStatus(status);
		}
		
		// content type
		// Alcune implementazioni richiedono di aggiornare il Content-Type
		try{
			if(msg!=null) {
				msg.updateContentType();
				contentTypeRisposta = msg.getContentType();
				if (contentTypeRisposta != null) {
					res.setContentType(contentTypeRisposta);
					TransportUtils.setHeader(trasporto,HttpConstants.CONTENT_TYPE, contentTypeRisposta);
				}
			}
		}catch(Throwable error){
			log.error("Errore durante la serializzazione del contentType: "+error.getMessage(),error);
			try{
				throw new ConnectorException(erroreIntegrazione.getDescrizione(protocolFactory),e);
			}catch(Throwable eInternal){
				// rilancio eccezione originale
				throw new ConnectorException(e.getMessage(),e);
			}
		}
		
		ConnectorDispatcherErrorInfo errorInfo = null;
		try{
			if(clientError) {
				errorInfo = ConnectorDispatcherErrorInfo.getClientError(msg, status, contentTypeRisposta, trasporto, requestInfo, protocolFactory);
			}else{
				errorInfo = ConnectorDispatcherErrorInfo.getGenericError(msg, status, contentTypeRisposta, trasporto, requestInfo, protocolFactory);	
			}
		}catch(Throwable error){
			log.error("Errore durante la generazione delle informazioni di errore: "+error.getMessage(),error);
			try{
				throw new ConnectorException(erroreIntegrazione.getDescrizione(protocolFactory),e);
			}catch(Throwable eInternal){
				// rilancio eccezione originale
				throw new ConnectorException(e.getMessage(),e);
			}
		}

		boolean consume = false; // può essere usato nel post out response handler
		try{
			res.sendResponse(msg, consume);
		}catch(Throwable error){
			log.error("Errore durante la serializzazione dell'errore: "+error.getMessage(),error);
			try{
				throw new ConnectorException(erroreIntegrazione.getDescrizione(protocolFactory),e);
			}catch(Throwable eInternal){
				// rilancio eccezione originale
				throw new ConnectorException(e.getMessage(),e);
			}
		}finally {
			res.flush(false);
			res.close(false);
		}
		
		return errorInfo;
	}
	
	
	public static void doInfo(ConnectorDispatcherInfo info, RequestInfo requestInfo,ConnectorOutMessage res, Logger log, boolean portaDelegata) throws ConnectorException{
		
		if(requestInfo==null) {
			throw new ConnectorException("RequestInfo param is null");
		}
		
		IProtocolFactory<?> protocolFactory = requestInfo.getProtocolFactory();
		
		OpenSPCoop2Message msg = info.getMessage();
		
		Map<String, List<String>> trasporto = info.getTrasporto();
		try {
			UtilitiesIntegrazione utilitiesIntegrazione = null;
			if(portaDelegata) {
				utilitiesIntegrazione = UtilitiesIntegrazione.getInstancePDResponse(log);
			}
			else {
				utilitiesIntegrazione = UtilitiesIntegrazione.getInstancePAResponse(log);
			}
			if(requestInfo!=null && requestInfo.getIdTransazione()!=null) {
				HeaderIntegrazione hdr = new HeaderIntegrazione(requestInfo.getIdTransazione());
				utilitiesIntegrazione.setTransportProperties(hdr,trasporto,null);
			}
			else {
				utilitiesIntegrazione.setInfoProductTransportProperties(trasporto);
			}
			utilitiesIntegrazione.setSecurityHeaders(msg!=null ? msg.getServiceBinding() : ServiceBinding.REST, requestInfo, trasporto, null);
			if(trasporto.size()>0){
				Iterator<String> keys = trasporto.keySet().iterator();
				while (keys.hasNext()) {
					String key = (String) keys.next();
					List<String> values = trasporto.get(key);
					if(values!=null && !values.isEmpty()) {
						for (String value : values) {
				    		res.addHeader(key,value);
						}
					}
		    	}	
			}
			
		}catch(Throwable error){
			log.error("Errore durante la serializzazione degli headers: "+error.getMessage(),error);
			try{
				throw new ConnectorException(ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.get5XX_ErroreProcessamento(error.getMessage())
						.getDescrizione(protocolFactory),error);
			}catch(Throwable eInternal){
				// rilancio eccezione originale
				throw new ConnectorException(error.getMessage(),error);
			}
		}
		
		int status = 200;
		String contentTypeRisposta = null;
		if(msg!=null && msg.getForcedResponseCode()!=null){
			status = Integer.parseInt(msg.getForcedResponseCode());
			res.setStatus(status);
		}
		else if(info.getStatus()>0) {
			status = info.getStatus();
			res.setStatus(status);
		}
		
		// content type
		// Alcune implementazioni richiedono di aggiornare il Content-Type
		try{
			if(msg!=null) {
				msg.updateContentType();
				contentTypeRisposta = msg.getContentType();
				if (contentTypeRisposta != null) {
					res.setContentType(contentTypeRisposta);
					TransportUtils.setHeader(trasporto,HttpConstants.CONTENT_TYPE, contentTypeRisposta);
				}
			}
		}catch(Throwable error){
			log.error("Errore durante la serializzazione del contentType: "+error.getMessage(),error);
			try{
				throw new ConnectorException(ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.get5XX_ErroreProcessamento(error.getMessage())
						.getDescrizione(protocolFactory),error);
			}catch(Throwable eInternal){
				// rilancio eccezione originale
				throw new ConnectorException(error.getMessage(),error);
			}
		}
	
		boolean consume = false; // può essere usato nel post out response handler
		try{
			res.sendResponse(msg, consume);
		}catch(Throwable error){
			log.error("Errore durante la serializzazione dell'errore: "+error.getMessage(),error);
			try{
				throw new ConnectorException(ErroriIntegrazione.ERRORE_5XX_GENERICO_PROCESSAMENTO_MESSAGGIO.get5XX_ErroreProcessamento(error.getMessage())
						.getDescrizione(protocolFactory),error);
			}catch(Throwable eInternal){
				// rilancio eccezione originale
				throw new ConnectorException(error.getMessage(),error);
			}
		}finally {
			res.flush(false);
			res.close(false);
		}
		
	}
}