StickyUtils.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */
  20. package org.openspcoop2.pdd.core.behaviour.built_in.load_balance.sticky;

  21. import java.io.ByteArrayOutputStream;
  22. import java.util.HashMap;
  23. import java.util.List;
  24. import java.util.Map;

  25. import javax.servlet.http.Cookie;
  26. import javax.servlet.http.HttpServletRequest;

  27. import org.apache.commons.lang.StringUtils;
  28. import org.openspcoop2.core.config.PortaApplicativa;
  29. import org.openspcoop2.core.config.Proprieta;
  30. import org.openspcoop2.core.id.IDPortaApplicativa;
  31. import org.openspcoop2.message.OpenSPCoop2Message;
  32. import org.openspcoop2.message.constants.MessageType;
  33. import org.openspcoop2.message.constants.ServiceBinding;
  34. import org.openspcoop2.pdd.config.ConfigurazionePdDManager;
  35. import org.openspcoop2.pdd.config.OpenSPCoop2Properties;
  36. import org.openspcoop2.pdd.core.CostantiPdD;
  37. import org.openspcoop2.pdd.core.PdDContext;
  38. import org.openspcoop2.pdd.core.behaviour.BehaviourEmitDiagnosticException;
  39. import org.openspcoop2.pdd.core.behaviour.BehaviourException;
  40. import org.openspcoop2.pdd.core.behaviour.BehaviourPropertiesUtils;
  41. import org.openspcoop2.pdd.core.behaviour.conditional.ConditionalUtils;
  42. import org.openspcoop2.pdd.core.dynamic.DynamicUtils;
  43. import org.openspcoop2.pdd.core.dynamic.ErrorHandler;
  44. import org.openspcoop2.pdd.core.dynamic.MessageContent;
  45. import org.openspcoop2.pdd.core.dynamic.Template;
  46. import org.openspcoop2.pdd.logger.MsgDiagnosticiProperties;
  47. import org.openspcoop2.pdd.logger.MsgDiagnostico;
  48. import org.openspcoop2.pdd.services.connector.FormUrlEncodedHttpServletRequest;
  49. import org.openspcoop2.protocol.sdk.Busta;
  50. import org.openspcoop2.protocol.sdk.state.IState;
  51. import org.openspcoop2.protocol.sdk.state.RequestInfo;
  52. import org.openspcoop2.utils.regexp.RegExpNotFoundException;
  53. import org.openspcoop2.utils.regexp.RegularExpressionEngine;
  54. import org.openspcoop2.utils.transport.TransportUtils;
  55. import org.openspcoop2.utils.transport.http.HttpServletTransportRequestContext;
  56. import org.openspcoop2.utils.xml.AbstractXPathExpressionEngine;
  57. import org.openspcoop2.utils.xml2json.JsonXmlPathExpressionEngine;
  58. import org.slf4j.Logger;

  59. /**
  60.  * ConditionalUtils
  61.  *
  62.  * @author Andrea Poli (apoli@link.it)
  63.  * @author $Author$
  64.  * @version $Rev$, $Date$
  65.  */
  66. public class StickyUtils  {
  67.    
  68.     public static StickyResult getStickyResult(PortaApplicativa pa, OpenSPCoop2Message message, Busta busta,
  69.             RequestInfo requestInfo, PdDContext pddContext,
  70.             MsgDiagnostico msgDiag, Logger log,
  71.             IState state) throws BehaviourException, BehaviourEmitDiagnosticException {
  72.        
  73.         if(isConfigurazioneSticky(pa, log)==false) {
  74.             return null; // non vi è da fare alcun filtro condizionale
  75.         }
  76.        
  77.         StickyResult result = new StickyResult();
  78.        
  79.         StickyConfigurazione config = read(pa, log);

  80.         StickyTipoSelettore tipoSelettore = config.getTipoSelettore();
  81.         String patternSelettore = config.getPattern();
  82.        
  83.         String pattern = "";
  84.         try {
  85.             Map<String, List<String>> pTrasporto = null;
  86.             String urlInvocazione = null;
  87.             Map<String, List<String>> pQuery = null;
  88.             Map<String, List<String>> pForm = null;
  89.             if(requestInfo!=null && requestInfo.getProtocolContext()!=null) {
  90.                 pTrasporto = requestInfo.getProtocolContext().getHeaders();
  91.                 urlInvocazione = requestInfo.getProtocolContext().getUrlInvocazione_formBased();
  92.                 pQuery = requestInfo.getProtocolContext().getParameters();
  93.                 if(requestInfo.getProtocolContext() instanceof HttpServletTransportRequestContext) {
  94.                     HttpServletTransportRequestContext httpServletContext = (HttpServletTransportRequestContext) requestInfo.getProtocolContext();
  95.                     HttpServletRequest httpServletRequest = httpServletContext.getHttpServletRequest();
  96.                     if(httpServletRequest!=null && httpServletRequest instanceof FormUrlEncodedHttpServletRequest) {
  97.                         FormUrlEncodedHttpServletRequest formServlet = (FormUrlEncodedHttpServletRequest) httpServletRequest;
  98.                         if(formServlet.getFormUrlEncodedParametersValues()!=null &&
  99.                                 !formServlet.getFormUrlEncodedParametersValues().isEmpty()) {
  100.                             pForm = formServlet.getFormUrlEncodedParametersValues();
  101.                         }
  102.                     }
  103.                 }
  104.             }
  105.             MessageContent messageContent = null;
  106.             boolean bufferMessage_readOnly =  OpenSPCoop2Properties.getInstance().isReadByPathBufferEnabled();
  107.             if(StickyTipoSelettore.CONTENT_BASED.equals(tipoSelettore) || tipoSelettore.isTemplate()) {
  108.                 if(ServiceBinding.SOAP.equals(message.getServiceBinding())){
  109.                     messageContent = new MessageContent(message.castAsSoap(), bufferMessage_readOnly, pddContext);
  110.                 }
  111.                 else{
  112.                     if(MessageType.XML.equals(message.getMessageType())){
  113.                         messageContent = new MessageContent(message.castAsRestXml(), bufferMessage_readOnly, pddContext);
  114.                     }
  115.                     else if(MessageType.JSON.equals(message.getMessageType())){
  116.                         messageContent = new MessageContent(message.castAsRestJson(), bufferMessage_readOnly, pddContext);
  117.                     }
  118.                     else{
  119.                         if(StickyTipoSelettore.CONTENT_BASED.equals(tipoSelettore)
  120.                                 // Nei template potrei utilizzare gli header o altre informazioni che non entrano nel merito del contenuto //|| tipoSelettore.isTemplate()
  121.                                 ) {
  122.                             throw new Exception("Selettore '"+tipoSelettore.getValue()+"' non supportato per il message-type '"+message.getMessageType()+"'");
  123.                         }
  124.                     }
  125.                 }
  126.             }
  127.            
  128.             msgDiag.addKeyword(CostantiPdD.KEY_TIPO_SELETTORE, tipoSelettore.getValue());
  129.             msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern); // per eliminare @@ dove non serve
  130.            
  131.             String condition = null;
  132.             switch (tipoSelettore) {
  133.            
  134.             case COOKIE_BASED:
  135.                 pattern = " (Cookie: "+patternSelettore+")";
  136.                 msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  137.                 StickyCookieConfig stickyCookie = new StickyCookieConfig(patternSelettore);
  138.                 if(requestInfo!=null && requestInfo.getProtocolContext()!=null && requestInfo.getProtocolContext().getHttpServletRequest()!=null) {
  139.                     Cookie [] cookies = requestInfo.getProtocolContext().getHttpServletRequest().getCookies();
  140.                     if(cookies!=null && cookies.length>0) {
  141.                         for (Cookie cookie : cookies) {
  142.                             if(cookie.getName().equalsIgnoreCase(stickyCookie.getName())) {
  143.                                
  144.                                 // NOTA: Nelle richieste il domani e il path non dovrebbero mai esserci. Lascio comunque il codice.
  145.                                
  146.                                 if(!StringUtils.isEmpty(stickyCookie.getDomain())) {
  147.                                     if(!stickyCookie.getDomain().equals(cookie.getDomain())) {
  148.                                         continue; // il cookie non ha un match sul domain
  149.                                     }
  150.                                 }
  151.                                 if(!StringUtils.isEmpty(stickyCookie.getPath())) {
  152.                                     if(!stickyCookie.getPath().equals(cookie.getPath())) {
  153.                                         continue; // il cookie non ha un match sul domain
  154.                                     }
  155.                                 }
  156.                                
  157.                                 condition = cookie.getValue();
  158.                                
  159.                                 if(cookie.getMaxAge()>0) {
  160.                                     result.setMaxAgeSeconds(cookie.getMaxAge());
  161.                                 }
  162.                                
  163.                                 break;
  164.                             }
  165.                         }
  166.                     }
  167.                 }
  168.                 if(condition==null) {
  169.                     throw new Exception("cookie non presente");
  170.                 }
  171.                 break;
  172.            
  173.             case HEADER_BASED:
  174.                 pattern = " (Header HTTP: "+patternSelettore+")";
  175.                 msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  176.                 condition = TransportUtils.getFirstValue(pTrasporto, patternSelettore);
  177.                 if(condition==null) {
  178.                     throw new Exception("header non presente");
  179.                 }
  180.                 break;
  181.                
  182.             case URLBASED:
  183.                 pattern = " (Espressione Regolare: "+patternSelettore+")";
  184.                 msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  185.                 try{
  186.                     condition = RegularExpressionEngine.getStringMatchPattern(urlInvocazione, patternSelettore);
  187.                 }catch(RegExpNotFoundException notFound){}
  188.                 break;
  189.                
  190.             case FORM_BASED:
  191.                 pattern = " (Parametro URL: "+patternSelettore+")";
  192.                 msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  193.                 condition = TransportUtils.getFirstValue(pQuery, patternSelettore);
  194.                 if(condition==null) {
  195.                     throw new Exception("parametro della url non presente");
  196.                 }
  197.                 break;
  198.                
  199.             case CONTENT_BASED:
  200.                 AbstractXPathExpressionEngine xPathEngine = null;
  201.                 if(messageContent==null) {
  202.                     throw new Exception("messaggio non presente");
  203.                 }
  204.                 if(messageContent.isXml()) {
  205.                     pattern = " (xPath: "+patternSelettore+")";
  206.                     msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  207.                     xPathEngine = new org.openspcoop2.message.xml.XPathExpressionEngine(message.getFactory());
  208.                     condition = AbstractXPathExpressionEngine.extractAndConvertResultAsString(messageContent.getElement(), xPathEngine, patternSelettore,  log);
  209.                 }
  210.                 else {
  211.                     pattern = " (jsonPath: "+patternSelettore+")";
  212.                     msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  213.                     condition = JsonXmlPathExpressionEngine.extractAndConvertResultAsString(messageContent.getElementJson(), patternSelettore, log);
  214.                 }
  215.                 break;
  216.                
  217.             case INDIRIZZO_IP:
  218.                 if(pddContext!=null && pddContext.containsKey(org.openspcoop2.core.constants.Costanti.CLIENT_IP_REMOTE_ADDRESS)) {
  219.                     condition = (String) pddContext.getObject(org.openspcoop2.core.constants.Costanti.CLIENT_IP_REMOTE_ADDRESS);
  220.                 }
  221.                 break;
  222.                
  223.             case INDIRIZZO_IP_FORWARDED:
  224.                 if(pddContext!=null && pddContext.containsKey(org.openspcoop2.core.constants.Costanti.CLIENT_IP_TRANSPORT_ADDRESS)) {
  225.                     condition = (String) pddContext.getObject(org.openspcoop2.core.constants.Costanti.CLIENT_IP_TRANSPORT_ADDRESS);
  226.                 }
  227.                 break;
  228.                
  229.             case TEMPLATE:
  230.                 if(patternSelettore.length()<50) {
  231.                     pattern = " ("+patternSelettore+")";
  232.                 }
  233.                 else {
  234.                     pattern = "";
  235.                 }
  236.                 msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  237.                 Map<String, Object> dynamicMap = new HashMap<>();
  238.                 ErrorHandler errorHandler = new ErrorHandler();
  239.                 DynamicUtils.fillDynamicMapRequest(log, dynamicMap, pddContext, urlInvocazione,
  240.                         message,
  241.                         messageContent,
  242.                         busta,
  243.                         pTrasporto,
  244.                         pQuery,
  245.                         pForm,
  246.                         errorHandler);
  247.                 condition = DynamicUtils.convertDynamicPropertyValue("ConditionalConfig.gwt", patternSelettore, dynamicMap, pddContext);
  248.                 if(condition!=null) {
  249.                     condition = ConditionalUtils.normalizeTemplateResult(condition);
  250.                 }
  251.                 break;
  252.                
  253.             case FREEMARKER_TEMPLATE:
  254.                 if(patternSelettore.length()<50) {
  255.                     pattern = " ("+patternSelettore+")";
  256.                 }
  257.                 else {
  258.                     pattern = "";
  259.                 }
  260.                 msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  261.                 dynamicMap = new HashMap<>();
  262.                 errorHandler = new ErrorHandler();
  263.                 DynamicUtils.fillDynamicMapRequest(log, dynamicMap, pddContext, urlInvocazione,
  264.                         message,
  265.                         messageContent,
  266.                         busta,
  267.                         pTrasporto,
  268.                         pQuery,
  269.                         pForm,
  270.                         errorHandler);
  271.                 ByteArrayOutputStream bout = new ByteArrayOutputStream();
  272.                 ConfigurazionePdDManager configurazionePdDManager = ConfigurazionePdDManager.getInstance(state);
  273.                 IDPortaApplicativa idPA = new IDPortaApplicativa();
  274.                 idPA.setNome(pa.getNome());
  275.                 Template template = configurazionePdDManager.getTemplateConnettoreMultiploSticky(idPA, patternSelettore.getBytes(), requestInfo);
  276.                 DynamicUtils.convertFreeMarkerTemplate(template, dynamicMap, bout);
  277.                 bout.flush();
  278.                 bout.close();
  279.                 condition = bout.toString();
  280.                 if(condition!=null) {
  281.                     condition = ConditionalUtils.normalizeTemplateResult(condition);
  282.                 }
  283.                 break;
  284.                
  285.             case VELOCITY_TEMPLATE:
  286.                 if(patternSelettore.length()<50) {
  287.                     pattern = " ("+patternSelettore+")";
  288.                 }
  289.                 else {
  290.                     pattern = "";
  291.                 }
  292.                 msgDiag.addKeyword(CostantiPdD.KEY_PATTERN_SELETTORE, pattern);
  293.                 dynamicMap = new HashMap<>();
  294.                 errorHandler = new ErrorHandler();
  295.                 DynamicUtils.fillDynamicMapRequest(log, dynamicMap, pddContext, urlInvocazione,
  296.                         message,
  297.                         messageContent,
  298.                         busta,
  299.                         pTrasporto,
  300.                         pQuery,
  301.                         pForm,
  302.                         errorHandler);
  303.                 bout = new ByteArrayOutputStream();
  304.                 configurazionePdDManager = ConfigurazionePdDManager.getInstance(state);
  305.                 idPA = new IDPortaApplicativa();
  306.                 idPA.setNome(pa.getNome());
  307.                 template = configurazionePdDManager.getTemplateConnettoreMultiploSticky(idPA, patternSelettore.getBytes(), requestInfo);
  308.                 DynamicUtils.convertVelocityTemplate(template, dynamicMap, bout);
  309.                 bout.flush();
  310.                 bout.close();
  311.                 condition = bout.toString();
  312.                 if(condition!=null) {
  313.                     condition = ConditionalUtils.normalizeTemplateResult(condition);
  314.                 }
  315.                 break;
  316.             }
  317.        
  318.             if(condition==null || "".equals(condition)) {
  319.                 throw new Exception("Nessuna condizione estratta");
  320.             }
  321.             else {
  322.                 result.setCondition(condition);
  323.                 result.setFound(true);              
  324.                
  325.                 msgDiag.addKeyword(CostantiPdD.KEY_CONDIZIONE_STICKY, condition);
  326.                
  327.                 msgDiag.logPersonalizzato(MsgDiagnosticiProperties.MSG_DIAG_CONSEGNA_CONTENUTI_APPLICATIVI,
  328.                         "connettoriMultipli.loadBalancer.sticky.identificazioneRiuscita");
  329.                
  330.             }
  331.            
  332.         }catch(Exception e) {
  333.            
  334.             msgDiag.addKeyword(CostantiPdD.KEY_ERRORE_PROCESSAMENTO, e.getMessage());
  335.            
  336.             msgDiag.logPersonalizzato(MsgDiagnosticiProperties.MSG_DIAG_CONSEGNA_CONTENUTI_APPLICATIVI,
  337.                             "connettoriMultipli.loadBalancer.sticky.identificazioneFallita");
  338.         }
  339.        
  340.        
  341.         if(result.isFound()) {
  342.             if(config.getMaxAgeSeconds()!=null) {
  343.                 if(result.getMaxAgeSeconds()==null) {
  344.                     result.setMaxAgeSeconds(config.getMaxAgeSeconds());
  345.                 }
  346.             }
  347.         }
  348.        
  349.         return result;
  350.     }
  351.    
  352.    
  353.     public static boolean isConfigurazioneSticky(PortaApplicativa pa, Logger log) {
  354.         if(pa.getBehaviour()==null || pa.getBehaviour().sizeProprietaList()<=0) {
  355.             return false;
  356.         }
  357.         String type = null;
  358.         for (Proprieta p : pa.getBehaviour().getProprietaList()) {
  359.             if(StickyCostanti.STICKY.equals(p.getNome())) {
  360.                 type = p.getValore();
  361.                 break;
  362.             }
  363.         }
  364.         if(type==null) {
  365.             return false;
  366.         }
  367.         return "true".equals(type);
  368.     }
  369.        
  370.     public static StickyConfigurazione read(PortaApplicativa pa, Logger log) throws BehaviourException {
  371.         StickyConfigurazione config = new StickyConfigurazione();
  372.         if(pa.getBehaviour()==null || pa.getBehaviour().sizeProprietaList()<=0) {
  373.             throw new BehaviourException("Configurazione sticky non disponibile");
  374.         }
  375.        
  376.         for (Proprieta p : pa.getBehaviour().getProprietaList()) {
  377.            
  378.             String nome = p.getNome();
  379.             String valore = p.getValore().trim();
  380.            
  381.             try {
  382.                 if(StickyCostanti.STICKY.equals(nome)) {
  383.                     config.setStickyEnabled("true".equals(valore));
  384.                 }
  385.                 else if(StickyCostanti.STICKY_TIPO_SELETTORE.equals(nome)) {
  386.                     config.setTipoSelettore(StickyTipoSelettore.toEnumConstant(valore, true));
  387.                 }
  388.                 else if(StickyCostanti.STICKY_PATTERN.equals(nome)) {
  389.                     config.setPattern(valore);
  390.                 }
  391.                 else if(StickyCostanti.STICKY_EXPIRE.equals(nome)) {
  392.                     config.setMaxAgeSeconds(Integer.valueOf(valore));
  393.                 }
  394.             }catch(Exception e) {
  395.                 throw new BehaviourException("Configurazione sticky non corretta (proprietà:"+p.getNome()+" valore:'"+p.getValore()+"'): "+e.getMessage(),e);
  396.             }
  397.            
  398.         }

  399.         return config;
  400.     }
  401.    

  402.     public static void save(PortaApplicativa pa, StickyConfigurazione configurazione) throws BehaviourException {
  403.        
  404.         if(pa.getBehaviour()==null) {
  405.             throw new BehaviourException("Configurazione behaviour non abilitata");
  406.         }
  407.         if(configurazione==null) {
  408.             throw new BehaviourException("Configurazione condizionale non fornita");
  409.         }
  410.         BehaviourPropertiesUtils.addProprieta(pa.getBehaviour(),StickyCostanti.STICKY, configurazione.isStickyEnabled()+"");
  411.        
  412.         BehaviourPropertiesUtils.removeProprieta(pa.getBehaviour(),StickyCostanti.STICKY_TIPO_SELETTORE);
  413.         BehaviourPropertiesUtils.removeProprieta(pa.getBehaviour(),StickyCostanti.STICKY_PATTERN);
  414.         BehaviourPropertiesUtils.removeProprieta(pa.getBehaviour(),StickyCostanti.STICKY_EXPIRE);
  415.        
  416.         if(configurazione.isStickyEnabled()) {
  417.             if(configurazione.getTipoSelettore()!=null) {
  418.                 BehaviourPropertiesUtils.addProprieta(pa.getBehaviour(),StickyCostanti.STICKY_TIPO_SELETTORE, configurazione.getTipoSelettore().getValue());
  419.             }
  420.    
  421.             if(StringUtils.isNotEmpty(configurazione.getPattern())) {
  422.                 BehaviourPropertiesUtils.addProprieta(pa.getBehaviour(),StickyCostanti.STICKY_PATTERN, configurazione.getPattern());
  423.             }
  424.            
  425.             if(configurazione.getMaxAgeSeconds()!=null && configurazione.getMaxAgeSeconds().intValue()>0) {
  426.                 BehaviourPropertiesUtils.addProprieta(pa.getBehaviour(),StickyCostanti.STICKY_EXPIRE, configurazione.getMaxAgeSeconds().intValue()+"");
  427.             }
  428.         }
  429.        
  430.     }
  431.    
  432. }