MTOMProcessor.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;
import org.slf4j.Logger;
import org.openspcoop2.core.config.constants.MTOMProcessorType;
import org.openspcoop2.core.constants.Costanti;
import org.openspcoop2.core.constants.TipoPdD;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.OpenSPCoop2SoapMessage;
import org.openspcoop2.message.constants.MessageRole;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.pdd.config.MTOMProcessorConfig;
import org.openspcoop2.pdd.config.MessageSecurityConfig;
import org.openspcoop2.pdd.core.transazioni.Transaction;
import org.openspcoop2.pdd.core.transazioni.TransactionContext;
import org.openspcoop2.pdd.core.transazioni.TransactionNotExistsException;
import org.openspcoop2.pdd.logger.MsgDiagnosticiProperties;
import org.openspcoop2.pdd.logger.MsgDiagnostico;
import org.openspcoop2.protocol.sdk.constants.RuoloMessaggio;
/**
* MTOMProcessor
*
* @author Andrea Poli (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class MTOMProcessor {
private MTOMProcessorConfig config;
private MessageSecurityConfig secConfig;
private TipoPdD tipoPdD;
private MsgDiagnostico msgDiag;
private Logger log;
private PdDContext pddContext;
private Transaction transactionNullable;
public MTOMProcessor(MTOMProcessorConfig config, MessageSecurityConfig secConfig, TipoPdD tipoPdD,
MsgDiagnostico msgDiag, Logger log, PdDContext pddContext){
this.config = config;
this.secConfig = secConfig;
this.tipoPdD = tipoPdD;
this.msgDiag = msgDiag;
this.log = log;
this.pddContext = pddContext;
if(this.pddContext!=null && this.pddContext.containsKey(Costanti.ID_TRANSAZIONE)) {
String idTransazione = (String) this.pddContext.getObject(Costanti.ID_TRANSAZIONE);
try {
this.transactionNullable = TransactionContext.getTransaction(idTransazione);
}catch(TransactionNotExistsException e) {
// Puo' succedere nelle comunicazioni stateful
}
}
}
public MTOMProcessorType getMTOMProcessorType(){
if(this.config!=null)
return this.config.getMtomProcessorType();
else
return null;
}
public void mtomBeforeSecurity(OpenSPCoop2Message msg,RuoloMessaggio tipo) throws Exception{
if(msg==null) {
return;
}
if(!ServiceBinding.SOAP.equals(msg.getServiceBinding())){
return;
}
boolean emitDiagDisabled = false;
if(this.isEngineEnabled()){
if(this.isMTOMBeforeSecurity(tipo)){
if(this.transactionNullable!=null) {
switch (tipo) {
case RICHIESTA:
this.transactionNullable.getTempiElaborazione().startGestioneAttachmentsRichiesta();
break;
case RISPOSTA:
this.transactionNullable.getTempiElaborazione().startGestioneAttachmentsRisposta();
break;
}
}
try {
this.setProcessorTypeIntoDiagnostic(tipo);
this.emitDiagnostic(tipo,
"mtom.processamentoRichiestaInCorso",
"mtom.processamentoRispostaInCorso");
try{
this.mtomApply(msg.castAsSoap());
this.emitDiagnostic(tipo,
"mtom.processamentoRichiestaEffettuato",
"mtom.processamentoRispostaEffettuato");
}catch(Exception e){
if(MessageRole.REQUEST.equals(msg.getMessageRole())) {
this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.ERRORE_ALLEGATI_MESSAGGIO_RICHIESTA, "true");
}
else {
this.pddContext.addObject(org.openspcoop2.core.constants.Costanti.ERRORE_ALLEGATI_MESSAGGIO_RISPOSTA, "true");
}
this.msgDiag.addKeywordErroreProcessamento(e);
this.log.error("[MTOM BeforeSecurity "+tipo.getTipo()+"] "+e.getMessage(),e);
this.emitDiagnostic(tipo,
"mtom.processamentoRichiestaInErrore",
"mtom.processamentoRispostaInErrore");
throw e;
}
}
finally {
if(this.transactionNullable!=null) {
switch (tipo) {
case RICHIESTA:
this.transactionNullable.getTempiElaborazione().endGestioneAttachmentsRichiesta();
break;
case RISPOSTA:
this.transactionNullable.getTempiElaborazione().endGestioneAttachmentsRisposta();
break;
}
}
}
}
else{
emitDiagDisabled = true;
}
}
else{
emitDiagDisabled = true;
}
if(emitDiagDisabled){
this.emitDiagnostic(tipo,
"mtom.beforeSecurity.processamentoRichiestaDisabilitato",
"mtom.beforeSecurity.processamentoRispostaDisabilitato");
}
}
public void mtomAfterSecurity(OpenSPCoop2Message msg,RuoloMessaggio tipo) throws Exception{
if(msg==null) {
return;
}
if(!ServiceBinding.SOAP.equals(msg.getServiceBinding())){
return;
}
boolean emitDiagDisabled = false;
if(this.isEngineEnabled()){
if(this.isMTOMBeforeSecurity(tipo)==false){
if(this.transactionNullable!=null) {
switch (tipo) {
case RICHIESTA:
this.transactionNullable.getTempiElaborazione().startGestioneAttachmentsRichiesta();
break;
case RISPOSTA:
this.transactionNullable.getTempiElaborazione().startGestioneAttachmentsRisposta();
break;
}
}
try {
this.setProcessorTypeIntoDiagnostic(tipo);
this.emitDiagnostic(tipo,
"mtom.processamentoRichiestaInCorso",
"mtom.processamentoRispostaInCorso");
try{
this.mtomApply(msg.castAsSoap());
this.emitDiagnostic(tipo,
"mtom.processamentoRichiestaEffettuato",
"mtom.processamentoRispostaEffettuato");
}catch(Exception e){
this.msgDiag.addKeywordErroreProcessamento(e);
this.log.error("[MTOM AfterSecurity "+tipo.getTipo()+"] "+e.getMessage(),e);
this.emitDiagnostic(tipo,
"mtom.processamentoRichiestaInErrore",
"mtom.processamentoRispostaInErrore");
throw e;
}
}
finally {
if(this.transactionNullable!=null) {
switch (tipo) {
case RICHIESTA:
this.transactionNullable.getTempiElaborazione().endGestioneAttachmentsRichiesta();
break;
case RISPOSTA:
this.transactionNullable.getTempiElaborazione().endGestioneAttachmentsRisposta();
break;
}
}
}
}
else{
emitDiagDisabled = true;
}
}
else{
emitDiagDisabled = true;
}
if(emitDiagDisabled){
this.emitDiagnostic(tipo,
"mtom.afterSecurity.processamentoRichiestaDisabilitato",
"mtom.afterSecurity.processamentoRispostaDisabilitato");
}
}
/* **** UTILITIES INTERNE ***** */
private void setProcessorTypeIntoDiagnostic(RuoloMessaggio tipo){
switch (tipo) {
case RICHIESTA:
this.msgDiag.addKeyword(CostantiPdD.KEY_TIPO_PROCESSAMENTO_MTOM_RICHIESTA, this.config.getMtomProcessorType().getValue());
this.pddContext.addObject(CostantiPdD.TIPO_PROCESSAMENTO_MTOM_RICHIESTA, this.config.getMtomProcessorType().getValue());
break;
case RISPOSTA:
this.msgDiag.addKeyword(CostantiPdD.KEY_TIPO_PROCESSAMENTO_MTOM_RISPOSTA, this.config.getMtomProcessorType().getValue());
this.pddContext.addObject(CostantiPdD.TIPO_PROCESSAMENTO_MTOM_RISPOSTA, this.config.getMtomProcessorType().getValue());
break;
}
}
private void emitDiagnostic(RuoloMessaggio tipo, String idDiagnosticRichiesta, String idDiagnosticRisposta){
// Il set del prefisso viene fatto poichè il processor viene usato anche in moduli (es. LocalForward) dove non è correttamente impostato
String originalPrefix = this.msgDiag.getPrefixMsgPersonalizzati();
try{
switch (this.tipoPdD) {
case DELEGATA:
this.msgDiag.setPrefixMsgPersonalizzati(MsgDiagnosticiProperties.MSG_DIAG_INOLTRO_BUSTE);
break;
case APPLICATIVA:
this.msgDiag.setPrefixMsgPersonalizzati(MsgDiagnosticiProperties.MSG_DIAG_RICEZIONE_BUSTE);
break;
default:
return; // nessun diagnostico
}
switch (tipo) {
case RICHIESTA:
this.msgDiag.logPersonalizzato(idDiagnosticRichiesta);
break;
case RISPOSTA:
this.msgDiag.logPersonalizzato(idDiagnosticRisposta);
break;
}
}finally{
this.msgDiag.setPrefixMsgPersonalizzati(originalPrefix);
}
}
private void mtomApply(OpenSPCoop2SoapMessage msg) throws Exception{
switch (this.config.getMtomProcessorType()) {
case PACKAGING:
if(this.config.getInfo()!=null && this.config.getInfo().size()>0){
msg.mtomPackaging(this.config.getInfo());
}
break;
case UNPACKAGING:
msg.mtomUnpackaging();
break;
case VERIFY:
if(this.config.getInfo()!=null && this.config.getInfo().size()>0){
msg.mtomVerify(this.config.getInfo());
}
break;
default:
break;
}
}
private boolean isMTOMBeforeSecurity(RuoloMessaggio tipoTraccia) throws Exception{
MTOMProcessorType processorType = null;
if(this.config!=null && this.config.getMtomProcessorType()!=null){
processorType = this.config.getMtomProcessorType();
}
else{
processorType = MTOMProcessorType.DISABLE;
}
// NOTA: per default la sicurezza viene sempre applicato prima del processo di packaging
// e dopo il processo di unpackaging trattando di fatto l'MTOM come un mero trasporto.
Boolean applyToMtom = null;
if(this.secConfig!=null && this.secConfig.getApplyToMtom()!=null){
applyToMtom = this.secConfig.getApplyToMtom();
}
switch (this.tipoPdD) {
case DELEGATA:
switch (tipoTraccia) {
case RICHIESTA:
switch (processorType) {
case DISABLE:
// caso che non puo' avvenire grazie al metodo isEngineEnabled
throw new Exception("Caso non previsto ["+processorType+"] Delegata.richiesta.disabile");
case PACKAGING:
if(applyToMtom==null){
return false; // (role:sender) primo cifro/firmo e poi applico packaging, in pratica mtom e' un mero trasporto (come se venisse chiamato sul connettore)
}
else if(applyToMtom){
return true; // (role:sender) prima applico il packaging in modo da applicare la sicurezza sul messaggio mtom
}else{
return false; // (role:sender) prima cifro/firmo e poi applico packaging, in pratica mtom e' un mero trasporto
}
case UNPACKAGING:
if(applyToMtom==null){
return false; // (role:sender) primo cifro/firmo e poi applico packaging, in pratica mtom e' un mero trasporto (come se venisse chiamato sul connettore)
}
else if(applyToMtom){
return false; // (role:sender) primo cifro/firmo e poi effettuo unpacking. Scenario senza senso !!!!
}else{
return true; // (role:sender) prima applico unpackaging poi cifro e firmo, in pratica mtom e' un mero trasporto (Sembra poco indicato al contesto della PdD)
}
case VERIFY:
return true; // (role:sender) prima verifico le references e poi applico la sicurezza (in modo da leggere eventuali elementi che saranno poi cifrati)
default:
throw new Exception("Caso non previsto Delegata.richiesta.["+processorType+"]");
}
case RISPOSTA:
switch (processorType) {
case DISABLE:
// caso che non puo' avvenire grazie al metodo isEngineEnabled
throw new Exception("Caso non previsto ["+processorType+"] Delegata.risposta.disabile");
case PACKAGING:
if(applyToMtom==null){
return true; // (role:receiver) prima applico il packaging e solo dopo verifico firma cifratura, in pratica mtom e' un mero trasporto (come se venisse chiamato sul connettore)
}
else if(applyToMtom){
return true; // (role:receiver) prima applico il packaging e solo dopo verifico firma e cifratura
}else{
return false; // (role:receiver) prima verifico firma cifratura, poi applico il packaging
}
case UNPACKAGING:
if(applyToMtom==null){
return true; // (role:receiver) primo applico l'unpackaging e solo dopo verifico firma cifratura, in pratica mtom e' un mero trasporto (come se venisse chiamato sul connettore)
}
else if(applyToMtom){
return false; // (role:receiver) prima verifico firma cifratura, poi applico il l'unpackaging
}else{
return true; // (role:receiver) prima applico l'unpackaging e solo dopo verifico firma e cifratura
}
case VERIFY:
return true; // (role:receiver) prima applico la sicurezza e solo dopo verifico (in modo da leggere eventuali elementi cifrati)
default:
throw new Exception("Caso non previsto Delegata.risposta.["+processorType+"]");
}
default:
throw new Exception("Tipo non gestito ["+tipoTraccia+"] in Delegata.risposta");
}
case APPLICATIVA:
switch (tipoTraccia) {
case RICHIESTA:
switch (processorType) {
case DISABLE:
// caso che non puo' avvenire grazie al metodo isEngineEnabled
throw new Exception("Caso non previsto ["+processorType+"] Applicativa.richiesta.disabile");
case PACKAGING:
if(applyToMtom==null){
return true; // (role:receiver) prima applico il packaging e solo dopo verifico firma cifratura, in pratica mtom e' un mero trasporto (come se venisse chiamato sul connettore)
}
else if(applyToMtom){
return true; // (role:receiver) prima applico il packaging e solo dopo verifico firma e cifratura
}else{
return false; // (role:receiver) prima verifico firma cifratura, poi applico il packaging
}
case UNPACKAGING:
if(applyToMtom==null){
return true; // (role:receiver) primo applico l'unpackaging e solo dopo verifico firma cifratura, in pratica mtom e' un mero trasporto (come se venisse chiamato sul connettore)
}
else if(applyToMtom){
return false; // (role:receiver) prima verifico firma cifratura, poi applico il l'unpackaging
}else{
return true; // (role:receiver) prima applico l'unpackaging e solo dopo verifico firma e cifratura
}
case VERIFY:
return true; // (role:receiver) prima applico la sicurezza e solo dopo verifico (in modo da leggere eventuali elementi cifrati)
default:
throw new Exception("Caso non previsto Applicativa.richiesta.["+processorType+"]");
}
case RISPOSTA:
switch (processorType) {
case DISABLE:
// caso che non puo' avvenire grazie al metodo isEngineEnabled
throw new Exception("Caso non previsto ["+processorType+"] Applicativa.risposta.disabile");
case PACKAGING:
if(applyToMtom==null){
return false; // (role:sender) primo cifro/firmo e poi applico packaging, in pratica mtom e' un mero trasporto (come se venisse chiamato sul connettore)
}
else if(applyToMtom){
return true; // (role:sender) prima applico il packaging in modo da applicare la sicurezza sul messaggio mtom
}else{
return false; // (role:sender) prima cifro/firmo e poi applico packaging, in pratica mtom e' un mero trasporto
}
case UNPACKAGING:
if(applyToMtom==null){
return false; // (role:sender) primo cifro/firmo e poi applico packaging, in pratica mtom e' un mero trasporto (come se venisse chiamato sul connettore)
}
else if(applyToMtom){
return false; // (role:sender) primo cifro/firmo e poi effettuo unpacking. Scenario senza senso !!!!
}else{
return true; // (role:sender) prima applico unpackaging poi cifro e firmo, in pratica mtom e' un mero trasporto (Sembra poco indicato al contesto della PdD)
}
case VERIFY:
return true; // (role:sender) prima verifico le references e poi applico la sicurezza (in modo da leggere eventuali elementi che saranno poi cifrati)
default:
throw new Exception("Caso non previsto Applicativa.risposta.["+processorType+"]");
}
default:
throw new Exception("Tipo non gestito ["+tipoTraccia+"] in Applicativa.risposta");
}
case INTEGRATION_MANAGER:
case ROUTER:
default:
throw new Exception("Ruolo ["+this.tipoPdD+"] non gestito");
}
}
private boolean isEngineEnabled(){
if(!TipoPdD.DELEGATA.equals(this.tipoPdD) && !TipoPdD.APPLICATIVA.equals(this.tipoPdD)){
return false;
}
if(this.config!=null && this.config.getMtomProcessorType()!=null && !MTOMProcessorType.DISABLE.equals(this.config.getMtomProcessorType())){
return true;
}
return false;
}
}