HttpServletConnectorOutMessage.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.messages;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.openspcoop2.core.config.PortaApplicativa;
import org.openspcoop2.core.config.PortaDelegata;
import org.openspcoop2.core.config.Proprieta;
import org.openspcoop2.core.id.IDPortaApplicativa;
import org.openspcoop2.core.id.IDPortaDelegata;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.OpenSPCoop2MessageProperties;
import org.openspcoop2.message.OpenSPCoop2RestMessage;
import org.openspcoop2.message.OpenSPCoop2SoapMessage;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.message.exception.MessageException;
import org.openspcoop2.pdd.config.ConfigurazionePdDManager;
import org.openspcoop2.pdd.config.CostantiProprieta;
import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
import org.openspcoop2.pdd.services.connector.ConnectorException;
import org.openspcoop2.protocol.sdk.IProtocolFactory;
import org.openspcoop2.protocol.sdk.constants.IDService;
import org.openspcoop2.protocol.sdk.state.RequestInfo;
import org.openspcoop2.utils.LoggerWrapperFactory;
import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.io.DumpByteArrayOutputStream;
import org.openspcoop2.utils.resources.Charset;
import org.openspcoop2.utils.transport.http.RFC2047Encoding;
import org.openspcoop2.utils.transport.http.RFC2047Utilities;
/**
* HttpServletConnectorOutMessage
*
* @author Andrea Poli (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class HttpServletConnectorOutMessage implements ConnectorOutMessage {
protected HttpServletResponse res;
protected OutputStream outNullable;
protected IProtocolFactory<?> protocolFactory;
protected RequestInfo requestInfo;
protected String idModulo;
protected IDService idModuloAsIDService;
protected OpenSPCoop2Properties openspcoopProperties;
public HttpServletConnectorOutMessage(RequestInfo requestInfo,IProtocolFactory<?> protocolFactory, HttpServletResponse res,
IDService idModuloAsIDService, String idModulo) throws ConnectorException{
try{
this.res = res;
this.protocolFactory = protocolFactory;
this.requestInfo = requestInfo;
this.idModuloAsIDService = idModuloAsIDService;
this.idModulo = idModulo;
this.openspcoopProperties = OpenSPCoop2Properties.getInstance();
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
private void sendHeadersEngine(OpenSPCoop2Message msg) throws ConnectorException, MessageException {
if(msg==null) {
throw new ConnectorException("Message is null");
}
// Propago eventuali header http
OpenSPCoop2MessageProperties forwardHeader = null;
if(ServiceBinding.REST.equals(msg.getServiceBinding())) {
forwardHeader = msg.getForwardTransportHeader(this.openspcoopProperties.getRESTServicesHeadersForwardConfig(false));
}
else {
forwardHeader = msg.getForwardTransportHeader(this.openspcoopProperties.getSOAPServicesHeadersForwardConfig(false));
}
if(forwardHeader!=null && forwardHeader.size()>0){
Iterator<String> keys = forwardHeader.getKeys();
while (keys.hasNext()) {
String key = keys.next();
List<String> values = forwardHeader.getPropertyValues(key);
if(values!=null && !values.isEmpty()) {
for (String value : values) {
this.addHeader(key, value);
}
}
}
}
}
@Override
public void sendResponse(OpenSPCoop2Message msg, boolean consume) throws ConnectorException {
try{
if(msg==null) {
throw new ConnectorException("Message is null");
}
// Propago eventuali header http
this.sendHeadersEngine(msg);
boolean hasContent = false;
// il save e' necessario con i connettori directVM in caso di errori di validazione
if(ServiceBinding.SOAP.equals(msg.getServiceBinding())){
hasContent = true;
OpenSPCoop2SoapMessage soap = msg.castAsSoap();
if(soap.hasSOAPFault()){
soap.saveChanges();
}
}
if(ServiceBinding.REST.equals(msg.getServiceBinding())){
OpenSPCoop2RestMessage<?> rest = msg.castAsRest();
hasContent = rest.hasContent();
}
if(hasContent) {
this.outNullable = this.res.getOutputStream();
msg.writeTo(this.outNullable,consume);
}
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
@Override
public void sendResponse(DumpByteArrayOutputStream message) throws ConnectorException{
try{
if(message!=null && message.size()>0) {
this.outNullable = this.res.getOutputStream();
if(message.isSerializedOnFileSystem()) {
try(FileInputStream fin = new FileInputStream(message.getSerializedFile())) {
Utilities.copy(fin, this.outNullable);
}
}
else {
this.outNullable.write(message.toByteArray());
}
}
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
@Override
public void sendResponseHeaders(OpenSPCoop2Message message) throws ConnectorException{
try{
// Propago eventuali header http
this.sendHeadersEngine(message);
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
@Override
public void setHeader(String key,String value) throws ConnectorException{
putHeaderEngine(key,value,false);
}
@Override
public void addHeader(String key,String value) throws ConnectorException{
putHeaderEngine(key,value,true);
}
private void putHeaderEngine(String key,String value, boolean add) throws ConnectorException{
try{
if(value==null) {
return;
}
boolean encodingRFC2047 = false;
Charset charsetRFC2047 = null;
RFC2047Encoding encodingAlgorithmRFC2047 = null;
boolean validazioneHeaderRFC2047 = false;
List<Proprieta> listProprieta = null;
if(this.idModuloAsIDService!=null){
switch (this.idModuloAsIDService) {
case PORTA_DELEGATA:
case PORTA_DELEGATA_INTEGRATION_MANAGER:
case PORTA_DELEGATA_XML_TO_SOAP:
encodingRFC2047 = this.openspcoopProperties.isEnabledEncodingRFC2047HeaderValueRicezioneContenutiApplicativi();
charsetRFC2047 = this.openspcoopProperties.getCharsetEncodingRFC2047HeaderValueRicezioneContenutiApplicativi();
encodingAlgorithmRFC2047 = this.openspcoopProperties.getEncodingRFC2047HeaderValueRicezioneContenutiApplicativi();
validazioneHeaderRFC2047 = this.openspcoopProperties.isEnabledValidazioneRFC2047HeaderNameValueRicezioneContenutiApplicativi();
listProprieta = readProprietaPortaDelegata();
break;
case PORTA_APPLICATIVA:
encodingRFC2047 = this.openspcoopProperties.isEnabledEncodingRFC2047HeaderValueRicezioneBuste();
charsetRFC2047 = this.openspcoopProperties.getCharsetEncodingRFC2047HeaderValueRicezioneBuste();
encodingAlgorithmRFC2047 = this.openspcoopProperties.getEncodingRFC2047HeaderValueRicezioneBuste();
validazioneHeaderRFC2047 = this.openspcoopProperties.isEnabledValidazioneRFC2047HeaderNameValueRicezioneBuste();
listProprieta = readProprietaPortaApplicativa();
break;
default:
break;
}
}
encodingRFC2047 = CostantiProprieta.isConnettoriHeaderValueEncodingRFC2047ResponseEnabled(listProprieta, encodingRFC2047);
charsetRFC2047 = CostantiProprieta.getConnettoriHeaderValueEncodingRFC2047ResponseCharset(listProprieta, charsetRFC2047);
encodingAlgorithmRFC2047 = CostantiProprieta.getConnettoriHeaderValueEncodingRFC2047ResponseType(listProprieta, encodingAlgorithmRFC2047);
validazioneHeaderRFC2047 = CostantiProprieta.isConnettoriHeaderValidationResponseEnabled(listProprieta, validazioneHeaderRFC2047);
/**System.out.println("@@@@ encodingRFC2047["+encodingRFC2047+"] charsetRFC2047["+charsetRFC2047+"] encodingAlgorithmRFC2047["+encodingAlgorithmRFC2047+"] validazioneHeaderRFC2047["+validazioneHeaderRFC2047+"]");*/
if(encodingRFC2047){
/**System.out.println("@@@@ CONTROLLO '"+value+" rispetto a '"+charsetRFC2047+"': "+RFC2047Utilities.isAllCharactersInCharset(value, charsetRFC2047));*/
if(!RFC2047Utilities.isAllCharactersInCharset(value, charsetRFC2047)){
String encoded = RFC2047Utilities.encode((value+""), charsetRFC2047, encodingAlgorithmRFC2047);
/**System.out.println("@@@@ RESPONSE CODIFICA ["+value+"] in ["+encoded+"]");*/
this.putResponseHeader(validazioneHeaderRFC2047, key, encoded, add);
}
else{
this.putResponseHeader(validazioneHeaderRFC2047, key, value, add);
}
}
else{
this.putResponseHeader(validazioneHeaderRFC2047, key, value, add);
}
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
private List<Proprieta> readProprietaPortaApplicativa(){
if(this.requestInfo!=null && this.requestInfo.getProtocolContext()!=null && this.requestInfo.getProtocolContext().getInterfaceName()!=null &&
StringUtils.isNotEmpty(this.requestInfo.getProtocolContext().getInterfaceName())) {
IDPortaApplicativa idPA = new IDPortaApplicativa();
idPA.setNome(this.requestInfo.getProtocolContext().getInterfaceName());
try {
PortaApplicativa pa = ConfigurazionePdDManager.getInstance().getPortaApplicativaSafeMethod(idPA, this.requestInfo);
if(pa!=null && pa.sizeProprieta()>0) {
return pa.getProprieta();
}
}catch(Exception e) {
if(this.protocolFactory!=null && this.protocolFactory.getLogger()!=null) {
this.protocolFactory.getLogger().error("Accesso porta applicativa ["+this.requestInfo.getProtocolContext().getInterfaceName()+"] fallito: "+e.getMessage(),e);
}
}
}
return null;
}
private List<Proprieta> readProprietaPortaDelegata(){
if(this.requestInfo!=null && this.requestInfo.getProtocolContext()!=null && this.requestInfo.getProtocolContext().getInterfaceName()!=null &&
StringUtils.isNotEmpty(this.requestInfo.getProtocolContext().getInterfaceName())) {
IDPortaDelegata idPD = new IDPortaDelegata();
idPD.setNome(this.requestInfo.getProtocolContext().getInterfaceName());
try {
PortaDelegata pd = ConfigurazionePdDManager.getInstance().getPortaDelegataSafeMethod(idPD, this.requestInfo);
if(pd!=null && pd.sizeProprieta()>0) {
return pd.getProprieta();
}
}catch(Exception e) {
if(this.protocolFactory!=null && this.protocolFactory.getLogger()!=null) {
this.protocolFactory.getLogger().error("Accesso porta applicativa ["+this.requestInfo.getProtocolContext().getInterfaceName()+"] fallito: "+e.getMessage(),e);
}
}
}
return null;
}
private void putResponseHeader(boolean validazioneHeaderRFC2047, String key, String value, boolean add) {
if(validazioneHeaderRFC2047){
try{
RFC2047Utilities.validHeader(key, value);
if(add) {
this.res.addHeader(key,value);
}
else {
this.res.setHeader(key,value);
}
}catch(UtilsException e){
if(this.protocolFactory!=null && this.protocolFactory.getLogger()!=null){
this.protocolFactory.getLogger().error(e.getMessage(),e);
}
else{
LoggerWrapperFactory.getLogger(HttpServletConnectorOutMessage.class).error(e.getMessage(),e);
}
}
}
else{
if(add) {
this.res.addHeader(key,value);
}
else {
this.res.setHeader(key,value);
}
}
}
@Override
public void setContentLength(int length) throws ConnectorException{
try{
this.res.setContentLength(length);
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
@Override
public void setContentType(String type) throws ConnectorException{
try{
this.res.setContentType(type);
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
private int status = -1;
@Override
public void setStatus(int status) throws ConnectorException{
try{
this.res.setStatus(status);
this.status = status;
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
@Override
public int getResponseStatus() throws ConnectorException{
return this.status;
}
@Override
public void flush(boolean throwException) throws ConnectorException{
try{
// Flush and close response
// NOTA: per poter ottenere l'errore di BrokenPipe sempre, deve essere disabilitato il socketBufferOutput sul servlet container.
// Se non lo si disabilta, l'errore viene ritornato solo se il messaggio supera la dimensione del buffer (default: 8192K)
// Ad esempio in tomcat utilizzare (socketBuffer="-1"):
// <Connector protocol="HTTP/1.1" port="8080" address="${jboss.bind.address}"
// connectionTimeout="20000" redirectPort="8443" socketBuffer="-1" />
if(this.res!=null){
try{
this.res.flushBuffer();
}catch(Exception e){
if(throwException){
throw e;
}
}
}
if(this.outNullable!=null){
try{
this.outNullable.flush();
}catch(Exception e){
if(throwException){
throw e;
}
}
}
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
@Override
public void close(boolean throwException) throws ConnectorException{
try{
if(this.outNullable!=null){
try{
this.outNullable.close();
this.outNullable = null;
}catch(Exception e){
if(throwException){
throw e;
}
}
}
}catch(Exception e){
throw new ConnectorException(e.getMessage(),e);
}
}
}