JmxDataSource.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.utils.datasource;

import java.util.Iterator;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.NotificationBroadcasterSupport;
import javax.management.ReflectionException;

import org.openspcoop2.utils.LoggerWrapperFactory;
import org.slf4j.Logger;




/**
 * Implementazione JMX per la gestione delle risorse
 *   
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class JmxDataSource extends NotificationBroadcasterSupport implements DynamicMBean {

	/** Nomi attributi */
	public static final String NUMERO_DATASOURCE = "datasources";
	
	/** Nomi metodi */
	public static final String CONNESSIONI_ALLOCATE = "getUsedConnections";
	public static final String INFORMAZIONI_DATABASE = "getInformazioniDatabase";
	public static final String DATASOURCE_CREATI = "getDatasources";
	public static final String DATASOURCE_CREATI_METHOD2 = "listDatasources"; // per farlo comparire in jmx-console

	
	public static final String MSG_OPERAZIONE_NON_EFFETTUATA = "Operazione non riuscita: ";
	
	/** getAttribute */
	@Override
	public Object getAttribute(String attributeName) throws AttributeNotFoundException,MBeanException,ReflectionException{
		
		if( (attributeName==null) || (attributeName.equals("")) )
			throw new IllegalArgumentException("Il nome dell'attributo e' nullo o vuoto");
		
		if(attributeName.equals(JmxDataSource.NUMERO_DATASOURCE))
			return DataSourceFactory.sizeDatasources();
		
		throw new AttributeNotFoundException("Attributo "+attributeName+" non trovato");
	}
	
	/** getAttributes */
	@Override
	public AttributeList getAttributes(String [] attributesNames){
		
		if(attributesNames==null)
			throw new IllegalArgumentException("Array nullo");
		
		AttributeList list = new AttributeList();
		for (int i=0; i<attributesNames.length; i++){
			try{
				list.add(new Attribute(attributesNames[i],getAttribute(attributesNames[i])));
			}catch(JMException ex){}
		}
		return list;
	}
	
	/** setAttribute */
	@Override
	public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException{
		
		if( attribute==null )
			throw new IllegalArgumentException("Il nome dell'attributo e' nullo");
		
		try{
			
			if(attribute.getName().equals(JmxDataSource.NUMERO_DATASOURCE)){
				// READ ONLY
			}
			else
				throw new AttributeNotFoundException("Attributo "+attribute.getName()+" non trovato");
			
		}catch(ClassCastException ce){
			throw new InvalidAttributeValueException("il tipo "+attribute.getValue().getClass()+" dell'attributo "+attribute.getName()+" non e' valido");
		}catch(JMException j){
			throw new MBeanException(j);
		}
		
	}
	
	/** setAttributes */
	@Override
	public AttributeList setAttributes(AttributeList list){
		
		if(list==null)
			throw new IllegalArgumentException("Lista degli attributi e' nulla");
		
		AttributeList ret = new AttributeList();
		Iterator<?> it = ret.iterator();
		
		while(it.hasNext()){
			try{
				Attribute attribute = (Attribute) it.next();
				setAttribute(attribute);
				ret.add(attribute);
			}catch(JMException ex){}
		}
		
		return ret;
		
	}
	
	/** invoke */
	@Override
	public Object invoke(String actionName, Object[]params, String[]signature) throws MBeanException,ReflectionException{
		
		if( (actionName==null) || (actionName.equals("")) )
			throw new IllegalArgumentException("Nessuna operazione definita");
		
		if(actionName.equals(JmxDataSource.CONNESSIONI_ALLOCATE)){
			
			if(params.length != 1)
				throw new MBeanException(new Exception("["+JmxDataSource.CONNESSIONI_ALLOCATE+"] Lunghezza parametri non corretta: "+params.length));
			
			String param1 = null;
			if(params[0]!=null && !"".equals(params[0])){
				param1 = (String)params[0];
			}
			if(param1==null){
				throw new MBeanException(new Exception("Identificativo del datasource non fornito"));
			}
			
			return this.getUsedDBConnections(param1);
		}
		
		else if(actionName.equals(JmxDataSource.INFORMAZIONI_DATABASE)){
			
			if(params.length != 1)
				throw new MBeanException(new Exception("["+JmxDataSource.INFORMAZIONI_DATABASE+"] Lunghezza parametri non corretta: "+params.length));
			
			String param1 = null;
			if(params[0]!=null && !"".equals(params[0])){
				param1 = (String)params[0];
			}
			if(param1==null){
				throw new MBeanException(new Exception("Identificativo del datasource non fornito"));
			}
			
			return this.getInformazioniDatabase(param1);
		}
		
		else if(actionName.equals(JmxDataSource.DATASOURCE_CREATI) || actionName.equals(JmxDataSource.DATASOURCE_CREATI_METHOD2)){
		
			return this.getDatasource();
			
		}
		
		throw new UnsupportedOperationException("Operazione "+actionName+" sconosciuta");
	}
	
	/* MBean info */
	@Override
	public MBeanInfo getMBeanInfo(){
		
		try{
		
			/*
			if(OpenSPCoopStartup.initialize){
				this.refresh();
			}
			*/
			
			// Per determinare se l'attributo e' leggibile/scrivibile
			final boolean READABLE = true;
			final boolean WRITABLE = true;
			
			// Per determinare se l'attributo e' ricavabile nella forma booleana isAttribute()
			final boolean IS_GETTER = true;
			
			// Descrizione della classe nel MBean
			String className = this.getClass().getName();
			String description = "Datasource allocati con la libreria org.openspcoop2.utils.datasource, premi pulsante (apply changes) per aggiornare i dati";
	
			// MetaData per l'attributo numMsgInConsegna
			MBeanAttributeInfo numDatasourceVAR 
				= new MBeanAttributeInfo(JmxDataSource.NUMERO_DATASOURCE,long.class.getName(),
							"Numero di Datasource allocati",
								READABLE,!WRITABLE,!IS_GETTER);
			
			// MetaData per l'operazione 
			MBeanOperationInfo getConnessioneAllocateOP 
				= new MBeanOperationInfo(JmxDataSource.CONNESSIONI_ALLOCATE,"Moduli funzionali che dispongono di una connessione verso il datasource fornito come parametro",
						new MBeanParameterInfo[]{
								new MBeanParameterInfo("idDatasource",String.class.getName(),"Identificativo del datasource")
							},
						String.class.getName(),
						MBeanOperationInfo.ACTION);
			
			// MetaData per l'operazione 
			MBeanOperationInfo getInformazioniDatabaseOP 
				= new MBeanOperationInfo(JmxDataSource.INFORMAZIONI_DATABASE,"Informazioni leggibili da una connessione ottenuta dal datasource fornito come parametro",
						new MBeanParameterInfo[]{
								new MBeanParameterInfo("idDatasource",String.class.getName(),"Identificativo del datasource")
							},
						String.class.getName(),
						MBeanOperationInfo.ACTION);
			
			// MetaData per l'operazione 
			MBeanOperationInfo getDatasourceAllocatiOP
				= new MBeanOperationInfo(JmxDataSource.DATASOURCE_CREATI,"Datasource allocati",
						null,
						String.class.getName(),
						MBeanOperationInfo.ACTION);
			
			// MetaData per l'operazione 
			MBeanOperationInfo getDatasourceAllocatiOP_method2
				= new MBeanOperationInfo(JmxDataSource.DATASOURCE_CREATI_METHOD2,"Elenca i datasource allocati",
						null,
						String.class.getName(),
						MBeanOperationInfo.ACTION);
			
			// Mbean costruttore
			MBeanConstructorInfo defaultConstructor = new MBeanConstructorInfo("Default Constructor","Crea e inizializza una nuova istanza del MBean",null);
	
			// Lista attributi
			MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[]{numDatasourceVAR};
			
			// Lista Costruttori
			MBeanConstructorInfo[] constructors = new MBeanConstructorInfo[]{defaultConstructor};
			
			// Lista operazioni
			MBeanOperationInfo[] operations = new MBeanOperationInfo[]{getDatasourceAllocatiOP, getDatasourceAllocatiOP_method2, getConnessioneAllocateOP, getInformazioniDatabaseOP};
			
			return new MBeanInfo(className,description,attributes,constructors,operations,null);
			
		}catch(Exception e){
			//System.out.println("ERRORE: "+e.getMessage());
			//e.printStackTrace(System.out);
			throw new RuntimeException(e.getMessage(),e);
		}
	}
	
	
	/* Variabili per la gestione JMX */
	private Logger log = LoggerWrapperFactory.getLogger(JmxDataSource.class);

	/* Costruttore */
	public JmxDataSource(){
		
	}
	
	
	/* Metodi di management JMX */

	public static final String MSG_NESSUNA_CONNESSIONE_ALLOCATA = "Nessuna connessione allocata";
	public static final String MSG_CONNESSIONI_ALLOCATE = " connessioni allocate: ";
	
	public static final String MSG_NESSUN_DATASOURCE_ALLOCATO = "Nessun datasource allocato";
	public static final String MSG_DATASOURCE_ALLOCATI = " datasource allocati: ";

	public String getUsedDBConnections(String uuidDataSource){
		String[] risorse = null;
		try{
			risorse = DataSourceFactory.getInstance(uuidDataSource).getJmxStatus();
		}catch(Throwable e){
			this.log.error(e.getMessage(),e);
			return MSG_OPERAZIONE_NON_EFFETTUATA+e.getMessage();
		}
		return getResultUsedDBConnections(risorse);
	}
	public static String getResultUsedDBConnections(String[] risorse) {
		if(risorse==null || risorse.length<=0)
			return MSG_NESSUNA_CONNESSIONE_ALLOCATA;
		
		StringBuilder bf = new StringBuilder();
		bf.append(risorse.length+MSG_CONNESSIONI_ALLOCATE+"\n");
		for(int i=0; i<risorse.length; i++){
			bf.append(risorse[i]+"\n");
		}
		return bf.toString();		
	}
	
	public String getInformazioniDatabase(String uuidDataSource){
		try{
			org.openspcoop2.utils.datasource.DataSource ds = DataSourceFactory.getInstance(uuidDataSource);
			return ds.getInformazioniDatabase();
		}catch(Throwable e){
			this.log.error(e.getMessage(),e);
			return MSG_OPERAZIONE_NON_EFFETTUATA+e.getMessage();
		}
	}
	
	public String getDatasource(){
		String[] risorse = null;
		try{
			risorse = DataSourceFactory.getJmxStatus();
		}catch(Throwable e){
			this.log.error(e.getMessage(),e);
			return MSG_OPERAZIONE_NON_EFFETTUATA+e.getMessage();
		}
		return getResultDatasource(risorse);
	}
	public static String getResultDatasource(String[] risorse) {
		if(risorse==null || risorse.length<=0)
			return MSG_NESSUN_DATASOURCE_ALLOCATO;
		
		StringBuilder bf = new StringBuilder();
		bf.append(risorse.length+MSG_DATASOURCE_ALLOCATI+"\n");
		for(int i=0; i<risorse.length; i++){
			bf.append(risorse[i]+"\n");
		}
		return bf.toString();
	}
	
}