PluginManager.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.monitor.engine.dynamic;

  21. import java.util.ArrayList;
  22. import java.util.Collections;
  23. import java.util.Date;
  24. import java.util.HashMap;
  25. import java.util.List;

  26. import org.apache.commons.lang.StringUtils;
  27. import org.openspcoop2.core.commons.CoreException;
  28. import org.openspcoop2.core.config.RegistroPlugin;
  29. import org.openspcoop2.core.config.RegistroPlugins;
  30. import org.openspcoop2.core.config.constants.StatoFunzionalita;
  31. import org.openspcoop2.generic_project.exception.NotFoundException;
  32. import org.openspcoop2.core.plugins.constants.TipoPlugin;
  33. import org.openspcoop2.utils.SemaphoreLock;
  34. import org.openspcoop2.utils.UtilsException;
  35. import org.openspcoop2.utils.date.DateManager;
  36. import org.slf4j.Logger;

  37. /**
  38.  * PluginManager
  39.  *
  40.  * @author Poli Andrea (apoli@link.it)
  41.  * @author $Author$
  42.  * @version $Rev$, $Date$
  43.  */
  44. public class PluginManager {

  45.     private IRegistroPluginsReader registroPluginsReader;
  46.    
  47.     private Date expireDate;
  48.     private int expireSeconds;
  49.     private final org.openspcoop2.utils.Semaphore lockExpire = new org.openspcoop2.utils.Semaphore("PluginManager-expire");
  50.    
  51.     private PluginsImage pluginsImage = new PluginsImage();
  52.     private PluginsImage pluginsImageSwitchOld = null;
  53.     private final org.openspcoop2.utils.Semaphore lockImage = new org.openspcoop2.utils.Semaphore("PluginManager-image");

  54.     protected PluginManager(IRegistroPluginsReader registroPluginsReader, int expireSeconds) {
  55.         this.registroPluginsReader = registroPluginsReader;
  56.         this.expireSeconds = expireSeconds;
  57.     }

  58.     private void checkUpdate(Logger log) {
  59.         try {
  60.             Date nowDate = DateManager.getDate();
  61.             if(this.expireDate==null || nowDate.after(this.expireDate)) {
  62.                 this.update(log, nowDate);
  63.             }
  64.         }catch(Exception t) {
  65.             log.error("Update plugin image failed: "+t.getMessage(),t);
  66.         }
  67.     }
  68.     public void updateFromConsoleConfig(Logger log) {
  69.         try {
  70.             if(this.expireDate!=null) {
  71.                 Date expired = new Date(this.expireDate.getTime()+1);
  72.                 this.update(log, expired);
  73.             }
  74.         }catch(Exception t) {
  75.             log.error("Update plugin image failed: "+t.getMessage(),t);
  76.         }
  77.     }
  78.     private void update(Logger log, Date nowDate) throws UtilsException, CoreException {
  79.        
  80.         boolean update = false;
  81.        
  82.         SemaphoreLock lock = this.lockExpire.acquire("updateExpireDate");
  83.         try {
  84.             if(this.expireDate==null || nowDate.after(this.expireDate)) {
  85.                 this.expireDate = new Date(nowDate.getTime()+(this.expireSeconds*1000));
  86.                 update = true;
  87.                 // l'aggiornamento effettivo lo faccio fuori dal synchronized per non bloccare le chiamate a _findClass, intanto che l'immagine viene aggiornata
  88.                 // gli altri thread che entrano in questo metodo trovano la lor nowDate inferiore ad expireDate
  89.             }
  90.         }finally {
  91.             this.lockExpire.release(lock, "updateExpireDate");
  92.         }
  93.        
  94.         if(update) {
  95.             RegistroPlugins registro = null;
  96.             try {
  97.                 registro = this.registroPluginsReader.getRegistroPlugins();
  98.             }catch (NotFoundException notFound) {
  99.                 log.debug(notFound.getMessage(),notFound);
  100.             }
  101.             this.update(log, registro);
  102.         }
  103.     }
  104.    
  105.     private void update(Logger log, RegistroPlugins pluginsParam) throws UtilsException {
  106.        
  107.         SemaphoreLock lock = this.lockImage.acquire("update");
  108.         try {
  109.            
  110.             RegistroPlugins plugins = null;
  111.             if(pluginsParam!=null) {
  112.                 plugins = pluginsParam;
  113.             }
  114.             else {
  115.                 plugins = new RegistroPlugins();
  116.             }
  117.    
  118.             // verifico prima quelli da eliminare
  119.             List<String> pluginDaEliminare = new ArrayList<>();
  120.             if(this.pluginsImage!=null && !this.pluginsImage.plugins.isEmpty()) {
  121.                 for (String pluginName : this.pluginsImage.plugins.keySet()) {
  122.                     boolean found = false;
  123.                     if(plugins.sizePluginList()>0) {
  124.                         for (RegistroPlugin pluginNew : plugins.getPluginList()) {
  125.                             if(pluginNew.getNome().equals(pluginName)) {
  126.                                 found = true;
  127.                                 break;
  128.                             }
  129.                         }
  130.                     }
  131.                     if(!found) {
  132.                         pluginDaEliminare.add(pluginName);
  133.                     }
  134.                 }
  135.             }
  136.            
  137.             // Nuova immagine
  138.             PluginsImage newImage = null;
  139.             if(plugins.sizePluginList()>0) {
  140.                 newImage = new PluginsImage();
  141.                 HashMap<String, String> mapPosizioniToNomi = new HashMap<>();
  142.                 for (RegistroPlugin pluginNew : plugins.getPluginList()) {
  143.                    
  144.                     if(!StatoFunzionalita.ABILITATO.equals(pluginNew.getStato())) {
  145.                         continue;
  146.                     }
  147.                     if(pluginNew.sizeArchivioList()<=0) {
  148.                         continue; // vuoto
  149.                     }
  150.                    
  151.                     String posPad = StringUtils.leftPad(pluginNew.getPosizione()+"", 10);
  152.                     mapPosizioniToNomi.put(posPad, pluginNew.getNome());
  153.                    
  154.                     // check se esiste
  155.                     if(this.pluginsImage!=null && this.pluginsImage.plugins.containsKey(pluginNew.getNome())) {
  156.                        
  157.                         // check se e' stato aggiornato
  158.                         Plugin active = this.pluginsImage.plugins.get(pluginNew.getNome());
  159.                         if(pluginNew.getData().after(active.getDate())) {
  160.                            
  161.                             // Da aggiornare
  162.                            
  163.                             pluginDaEliminare.add(pluginNew.getNome()); // elimino nella vecchia immagine
  164.                            
  165.                             Plugin pluginNewInstance = null;
  166.                             try {
  167.                                 pluginNewInstance = new Plugin(pluginNew);
  168.                             }catch(Exception e) {
  169.                                 log.error("Errore durante l'istanziazione del plugin '"+pluginNew.getNome()+"': "+e.getMessage(),e);
  170.                             }
  171.                             if(pluginNewInstance!=null) {
  172.                                 newImage.plugins.put(pluginNew.getNome(), pluginNewInstance);
  173.                             }
  174.                        
  175.                         }
  176.                         else {
  177.                            
  178.                             // non modificato
  179.                             newImage.plugins.put(pluginNew.getNome(), active);
  180.                            
  181.                         }
  182.                        
  183.                     }
  184.                     else {
  185.                         Plugin pluginNewInstance = null;
  186.                         try {
  187.                             pluginNewInstance = new Plugin(pluginNew);
  188.                         }catch(Exception e) {
  189.                             log.error("Errore durante l'istanziazione del plugin '"+pluginNew.getNome()+"': "+e.getMessage(),e);
  190.                         }
  191.                         if(pluginNewInstance!=null) {
  192.                             newImage.plugins.put(pluginNew.getNome(), pluginNewInstance);
  193.                         }
  194.                     }
  195.                    
  196.                 }
  197.                
  198.    
  199.                 // ordino per posizione
  200.                 if(!mapPosizioniToNomi.isEmpty()) {
  201.                     List<String> posizioni = new ArrayList<>();
  202.                     posizioni.addAll(mapPosizioniToNomi.keySet());
  203.                     Collections.sort(posizioni);
  204.                     for (String pos : posizioni) {
  205.                         newImage.pluginsActiveOrdered.add(mapPosizioniToNomi.get(pos));
  206.                     }
  207.                 }
  208.             }
  209.            
  210.             // effettuo switch
  211.             this.pluginsImageSwitchOld = this.pluginsImage;
  212.             this.pluginsImage = newImage;
  213.            
  214.             // Effettuo la chiusura di quelli da eliminare
  215.             if(!pluginDaEliminare.isEmpty()) {
  216.                 for (String pluginName : pluginDaEliminare) {
  217.                     try {
  218.                         this.pluginsImageSwitchOld.plugins.get(pluginName).close();
  219.                     }catch(Exception t) {
  220.                         // ignore
  221.                     }
  222.                 }
  223.             }
  224.             this.pluginsImageSwitchOld = null;
  225.                    
  226.         }finally {
  227.             this.lockImage.release(lock, "update");
  228.         }
  229.        
  230.     }
  231.    
  232.     public void close() {
  233.        
  234.         // Chiusura di tutti
  235.         SemaphoreLock lock = this.lockImage.acquireThrowRuntime("close");
  236.         try {
  237.            
  238.             if(this.pluginsImage!=null && this.pluginsImage.plugins.size()>0) {
  239.                 for (Plugin plugin : this.pluginsImage.plugins.values()) {
  240.                     try {
  241.                         plugin.close();
  242.                     }catch(Exception t) {
  243.                         // close
  244.                     }
  245.                 }
  246.             }
  247.            
  248.             if(this.pluginsImageSwitchOld!=null &&
  249.                 this.pluginsImageSwitchOld.plugins.size()>0) {
  250.                 for (Plugin plugin : this.pluginsImageSwitchOld.plugins.values()) {
  251.                     try {
  252.                         plugin.close();
  253.                     }catch(Exception t) {
  254.                         // close
  255.                     }
  256.                 }
  257.             }
  258.            
  259.         }finally {
  260.             this.lockImage.release(lock, "close");
  261.         }
  262.     }
  263.    

  264.     public Class<?> findClass(Logger log, TipoPlugin tipoClasseDaRicercare, String className) throws ClassNotFoundException {
  265.         return this.findClassEngine(log, tipoClasseDaRicercare, null, className, true);
  266.     }
  267.     public Class<?> findClass(Logger log, TipoPlugin tipoClasseDaRicercare, String className, boolean searchDefaultClassLoader) throws ClassNotFoundException {
  268.         return this.findClassEngine(log, tipoClasseDaRicercare, null, className, searchDefaultClassLoader);
  269.     }
  270.    
  271.     public Class<?> findClass(Logger log, String tipoClasseDaRicercare, String className) throws ClassNotFoundException {
  272.         return this.findClassEngine(log, null, tipoClasseDaRicercare, className, true);
  273.     }
  274.     public Class<?> findClass(Logger log, String tipoClasseDaRicercare, String className, boolean searchDefaultClassLoader) throws ClassNotFoundException {
  275.         return this.findClassEngine(log, null, tipoClasseDaRicercare, className, searchDefaultClassLoader);
  276.     }
  277.    
  278.    
  279.    
  280.     private Class<?> findClassEngine(Logger log, TipoPlugin tipoClasseDaRicercare,String tipoClasseCustomDaRicercare, String className, boolean searchDefaultClassLoader) throws ClassNotFoundException {

  281.         checkUpdate(log);
  282.        
  283.         // Se server gestire tramite una cache

  284.         // Se abilitato prima cerco sempre nel classloader attuale.
  285.         // Il classloader dinamico verrà utilizzato SOLAMENTE se il tipo non viene risolto prima tramite i meccanismi standard.
  286.         // Questo behaviour permette di avere poi un synchronized sul semaforo del classloader dinamico solamente quando serve davvero,
  287.         // visto che il caricamento dinamico delle classi è molto diffuso all'interno dell'architettura di GovWay.
  288.         ClassNotFoundException notFound = null;
  289.         if(searchDefaultClassLoader) {
  290.             try {
  291.                 return Class.forName(className);
  292.             }catch(ClassNotFoundException e) {
  293.                 notFound = e;
  294.             }
  295.         }
  296.        
  297.         if(this.pluginsImage!=null) { // potrebbe essere disabilitato
  298.             PluginsImage image = this.pluginsImage; // lo assegno, in modo che se avviene un update, cambia il riferimento
  299.            
  300.             if(!image.pluginsActiveOrdered.isEmpty()) {
  301.                 Class<?> c = findClassEngine(image, tipoClasseDaRicercare, tipoClasseCustomDaRicercare, className);
  302.                 if(c!=null) {
  303.                     return c;
  304.                 }
  305.             }
  306.         }
  307.        
  308.         if(notFound!=null) {
  309.             throw notFound;
  310.         }
  311.         return null;
  312.     }
  313.     private Class<?> findClassEngine(PluginsImage image, TipoPlugin tipoClasseDaRicercare, String tipoClasseCustomDaRicercare, String className){
  314.         List<String> listPluginsActiveOrdered = image.pluginsActiveOrdered;
  315.         for (String pluginName : listPluginsActiveOrdered) {
  316.            
  317.             Plugin plugin = image.plugins.get(pluginName);
  318.             if(plugin!=null) {
  319.                 Class<?> c = findClassEngineByPlugin(plugin, tipoClasseDaRicercare, tipoClasseCustomDaRicercare, className);
  320.                 if(c!=null) {
  321.                     return c;
  322.                 }
  323.             }
  324.            
  325.         }
  326.         return null;
  327.     }
  328.     private Class<?> findClassEngineByPlugin(Plugin plugin, TipoPlugin tipoClasseDaRicercare, String tipoClasseCustomDaRicercare, String className){
  329.         ClassLoader classLoader = null;
  330.         if(tipoClasseDaRicercare!=null) {
  331.             classLoader = plugin.getClassLoader(tipoClasseDaRicercare);
  332.         }else {
  333.             classLoader = plugin.getClassLoader(tipoClasseCustomDaRicercare);
  334.         }
  335.         if(classLoader!=null) {
  336.             Class<?> c = null;
  337.             try {
  338.                 c = classLoader.loadClass(className);
  339.             }catch(ClassNotFoundException cNotFound) {
  340.                 // ignore
  341.             }
  342.             if(c!=null) {
  343.                 return c;
  344.             }
  345.         }
  346.         return null;
  347.     }

  348. }

  349. class PluginsImage {
  350.    
  351.     HashMap<String, Plugin> plugins = new HashMap<>();
  352.     List<String> pluginsActiveOrdered = new ArrayList<>();
  353.    
  354. }