DataSourceFactory.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.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import javax.naming.RefAddr;

import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.UtilsAlreadyExistsException;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.date.DateUtils;
import org.openspcoop2.utils.jmx.RisorseJMXException;
import org.openspcoop2.utils.resources.GestoreJNDI;

/**
 * DatasourceFactory
 *   
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */
public class DataSourceFactory {

	private static Map<String, org.openspcoop2.utils.datasource.DataSource> mapUUIDtoDatasources = new ConcurrentHashMap<String, org.openspcoop2.utils.datasource.DataSource>();
	private static Map<String, String> mapApplicativeIDtoUUID = new ConcurrentHashMap<String, String>();
	private static Map<String, String> mapJndiNametoUUID = new ConcurrentHashMap<String, String>();
	private static org.openspcoop2.utils.jmx.GestoreRisorseJMX gestoreRisorse = null;
	
	private static synchronized void initGestoreRisorseJMX() throws RisorseJMXException{
		if(gestoreRisorse==null)
			gestoreRisorse = new org.openspcoop2.utils.jmx.GestoreRisorseJMX();
	}
	
	public static int sizeDatasources(){
		return mapUUIDtoDatasources.size();
	}
	
	public static List<String> getJndiNameDatasources() {
		if(mapJndiNametoUUID==null || mapJndiNametoUUID.isEmpty()) {
			return null;
		}
		List<String> l = new ArrayList<>();
		l.addAll(mapJndiNametoUUID.keySet());
		return l;
	}
	
	public static List<String> getApplicativeIdDatasources() {
		if(mapApplicativeIDtoUUID==null || mapApplicativeIDtoUUID.isEmpty()) {
			return null;
		}
		List<String> l = new ArrayList<>();
		l.addAll(mapApplicativeIDtoUUID.keySet());
		return l;
	}
	
	public static String[] getJmxStatus() throws UtilsException{	
		if(mapUUIDtoDatasources==null || mapUUIDtoDatasources.size()<=0)
			return null;
	
		Collection<org.openspcoop2.utils.datasource.DataSource> list = mapUUIDtoDatasources.values();
		Iterator<org.openspcoop2.utils.datasource.DataSource> it = list.iterator();
		List<String> listResource = new ArrayList<>();
		while (it.hasNext()) {
			org.openspcoop2.utils.datasource.DataSource datasource = (org.openspcoop2.utils.datasource.DataSource) it.next();
			StringBuilder bf = new StringBuilder();
			SimpleDateFormat dateformat = DateUtils.getSimpleDateFormatMs();
			bf.append("(").append(dateformat.format(datasource.getDate())).append(") ");
			bf.append("idDatasource:");
			bf.append(datasource.getUuidDatasource());
			if(datasource.getJndiName()!=null){
				if(bf.length() > 0){
					bf.append(" ");
				}
				bf.append("jndiName:");
				bf.append(datasource.getJndiName());
			}
			if(datasource.getApplicativeIdDatasource()!=null){
				if(bf.length() > 0){
					bf.append(" ");
				}
				bf.append("idApplicativo:");
				bf.append(datasource.getApplicativeIdDatasource());
			}
			if(bf.length() > 0){
				bf.append(" ");
			}
			bf.append("ConnessioniAttive:");
			bf.append(datasource.size());
			listResource.add(bf.toString());
		}
		if(listResource.size()>0){
			Collections.sort(listResource);
			return listResource.toArray(new String[1]);
		}else
			return null;
		
	}
	
	
	// **** Metodi per la creazione ***
	
	public static org.openspcoop2.utils.datasource.DataSource newInstance(String jndiName,Properties jndiContext, DataSourceParams params) throws UtilsException, UtilsAlreadyExistsException{
		if(jndiContext==null){
			jndiContext = new Properties();
		}
		GestoreJNDI jndi = new GestoreJNDI(jndiContext);
		Object oSearch = null;
		try{
			oSearch = jndi.lookup(jndiName);
		}catch(Throwable t){
			throw new UtilsException("Lookup jndiResource ["+jndiName+"] failed: "+t.getMessage(),t);
		}
		if(oSearch==null){
			throw new UtilsException("Lookup jndiResource ["+jndiName+"] not found");
		}
		javax.sql.DataSource datasource = null;
		try{
			datasource = (javax.sql.DataSource) oSearch;
		}catch(Throwable t){
			StringBuilder bf = new StringBuilder();
			if(oSearch instanceof javax.naming.Reference){
				javax.naming.Reference r = (javax.naming.Reference) oSearch;
				bf.append(" (Factory=");
				bf.append(r.getFactoryClassName());
				bf.append(" FactoryLocation=");
				bf.append(r.getFactoryClassLocation());
				Enumeration<RefAddr> enR = r.getAll();
				if(enR!=null){
					while (enR.hasMoreElements()) {
						RefAddr refAddr = (RefAddr) enR.nextElement();
						bf.append(" [").
							append("type=").
							append(refAddr.getType()).
							append(" content=").
							append(refAddr.getContent()).
							append("]");
					}
				}
				bf.append(")");
			}
			throw new UtilsException("lookup failed (object class: "+oSearch.getClass().getName()+")"+bf.toString()+": "+t.getMessage(),t);
		}
		
		return newInstance(datasource, params, jndiName);
	}

	public static org.openspcoop2.utils.datasource.DataSource newInstance(javax.sql.DataSource datasource, DataSourceParams params) throws UtilsException, UtilsAlreadyExistsException{
		return newInstance(datasource, params, null);
	}
	
	public static synchronized DataSource newInstance(javax.sql.DataSource datasource, DataSourceParams params, String jndiName) throws UtilsException, UtilsAlreadyExistsException{
		
		if(params==null){
			throw new UtilsException("Parameters undefined");
		}
		if(params.getDatabaseType()==null){
			throw new UtilsException("Parameters.databaseType undefined");
		}
		try{
			if(params.getApplicativeId()!=null){
				if(mapApplicativeIDtoUUID.containsKey(params.getApplicativeId())){
					throw new UtilsAlreadyExistsException("Datasource with applicative id ["+params.getApplicativeId()+"] already exists");
				}
			}
			
			if(jndiName!=null){
				if(mapJndiNametoUUID.containsKey(jndiName)){
					throw new UtilsAlreadyExistsException("Datasource with jndiName ["+jndiName+"] already exists");
				}
			}
			
			org.openspcoop2.utils.datasource.DataSource ds = 
					new org.openspcoop2.utils.datasource.DataSource(datasource, params.getDatabaseType(), params.isWrapOriginalMethods(), jndiName, params.getApplicativeId());
			
			String uuidDatasource = ds.getUuidDatasource();
			mapUUIDtoDatasources.put(uuidDatasource, ds);
			if(params.getApplicativeId()!=null){
				mapApplicativeIDtoUUID.put(params.getApplicativeId(), uuidDatasource);
			}
			if(jndiName!=null){
				mapJndiNametoUUID.put(jndiName, uuidDatasource);
			}
		
			if(params.isBindJmx()){
				if(gestoreRisorse==null){
					initGestoreRisorseJMX();
					gestoreRisorse.registerMBean(JmxDataSource.class, params.getJmxDomain(), params.getJmxType(), params.getJmxName(), false);
				}
			}
			
			return ds;
		}
		catch(UtilsAlreadyExistsException e){
			throw e;
		}
		catch(Exception e){
			throw new UtilsException(e.getMessage(),e);
		}
		
	}
	
	// **** Metodi per recuperare il datasource ***
	
	public static org.openspcoop2.utils.datasource.DataSource getInstance(String id) throws UtilsException{
		String uuid = id;
		if(mapApplicativeIDtoUUID.containsKey(id)){
			uuid = mapApplicativeIDtoUUID.get(id);
		}
		else if(mapJndiNametoUUID.containsKey(id)){
			uuid = mapJndiNametoUUID.get(id);
		}
		org.openspcoop2.utils.datasource.DataSource d = mapUUIDtoDatasources.get(uuid);
		if(d==null){
			throw new UtilsException("Datasource with id ["+id+"] not exists");
		}
		return d;
	}

	// **** Metodi per rilasciare le risorse ***
	public static void closeResources() throws UtilsException{

		mapApplicativeIDtoUUID.clear();
		mapJndiNametoUUID.clear();

		Collection<org.openspcoop2.utils.datasource.DataSource> list = mapUUIDtoDatasources.values();
		Iterator<org.openspcoop2.utils.datasource.DataSource> it = list.iterator();
		while (it.hasNext()) {
			org.openspcoop2.utils.datasource.DataSource datasource = (org.openspcoop2.utils.datasource.DataSource) it.next();
			datasource.setClosed(true);
		}
		
		boolean waitCloseConnection = true;
		int maxWait = 60000;
		int sleep = 1000;
		int index = 0;
		int count = 1;
		int total = maxWait / sleep;
		while(waitCloseConnection && index<maxWait){
			list = mapUUIDtoDatasources.values();
			it = list.iterator();
			boolean closeAll = true;
			StringBuilder bf = new StringBuilder();
			boolean debugClose = true;
			while (it.hasNext()) {
				org.openspcoop2.utils.datasource.DataSource datasource = (org.openspcoop2.utils.datasource.DataSource) it.next();
				if(datasource.size()>0){
					closeAll = false;
					bf.append("Find datasource (applicative-id:"+datasource.getApplicativeIdDatasource()+" jndi:"+datasource.getJndiName()+") with "+datasource.size()+" released connection:");
					String[] status = datasource.getJmxStatus();
					if(status!=null) {
						for (int i = 0; i < status.length; i++) {
							bf.append("\n");
							bf.append("\t"+status[i]);
						}
					}
					break;
				}
			}
			if(closeAll==false){
				if(debugClose && ((count%5)==0)) {
					// stampo la situazione ogni 5 secondi
					System.out.println(bf.toString());
				}
				System.out.println("Wait close connection ("+count+"/"+total+") ...");
				Utilities.sleep(sleep);
				index = index + sleep;
				count++;
			}
			else{
				waitCloseConnection = false;
			}
		}
		if(waitCloseConnection==true){
			System.out.println("Forzo chiusura connessioni");
			// forzo chiusura connessioni
			list = mapUUIDtoDatasources.values();
			it = list.iterator();
			while (it.hasNext()) {
				org.openspcoop2.utils.datasource.DataSource datasource = (org.openspcoop2.utils.datasource.DataSource) it.next();
				if(datasource.size()>0){
					datasource.releaseConnnections();
				}
			}
		}
		mapUUIDtoDatasources.clear();	
		
		if(gestoreRisorse!=null){
			gestoreRisorse.unregisterMBeans();
		}
	}
}