WizardAPIUtilities.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.protocol.engine.archive;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.openspcoop2.core.registry.AccordoServizioParteComune;
import org.openspcoop2.core.registry.GruppiAccordo;
import org.openspcoop2.core.registry.GruppoAccordo;
import org.openspcoop2.core.registry.IdSoggetto;
import org.openspcoop2.core.registry.ProtocolProperty;
import org.openspcoop2.core.registry.constants.FormatoSpecifica;
import org.openspcoop2.core.registry.constants.ProfiloCollaborazione;
import org.openspcoop2.core.registry.constants.ServiceBinding;
import org.openspcoop2.core.registry.utils.CleanerOpenSPCoop2Extensions;
import org.openspcoop2.protocol.engine.ProtocolFactoryManager;
import org.openspcoop2.protocol.information_missing.constants.Costanti;
import org.openspcoop2.protocol.sdk.ConfigurazionePdD;
import org.openspcoop2.protocol.sdk.IProtocolFactory;
import org.openspcoop2.protocol.sdk.ProtocolException;
import org.openspcoop2.utils.LoggerWrapperFactory;
import org.openspcoop2.utils.beans.WriteToSerializerType;
import org.openspcoop2.utils.resources.FileSystemUtilities;
import org.openspcoop2.utils.resources.Loader;
import org.slf4j.Logger;

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

	private static final String EMPTY = "Empty?";
	private static final String SUFFIX_NON_ESISTENTE = " non esistente";
	private static final String SUFFIX_NON_LEGGIBILE = " non leggibile";
	
	// Use: java WizardAPIUtilities outputFile profile pathInterface pathProperties [pathProfileProperties]
	private static final String USE = "Usage: java WizardAPIUtilities pathOutputFile profile pathInterface pathProperties [pathProfileProperties]";
	
	private static final Logger log = LoggerWrapperFactory.getLogger(WizardAPIUtilities.class);
	
	private static final String PROPERTIES_NAME = "name";
	private static final String PROPERTIES_VERSION = "version";
	private static final String PROPERTIES_DESCRIPTION = "description";
	private static final String PROPERTIES_SERVICE_BINDING = "serviceBinding";
	private static final String PROPERTIES_INTERFACE_TYPE = "interfaceType";
	private static final String PROPERTIES_TAG = "tag";
	private static final String PROPERTIES_CANALE = "canale";
	
	public static void main(String[] args) throws ProtocolException {
		
		if(args==null || args.length<4) {
			throw new ProtocolException("ERROR: argomenti non forniti\n"+USE);
		}
		
		String pathOutputFile = readPathOutpuFile(args, 0);
		
		String profile = readProfile(args, 1);
		
		byte[] bytesInterface = readInterface(args, 2);
		
		Properties properties = readProperties(args, 3);
		
		Properties profileProperties = readProfileProperties(args, 4);
		
		IProtocolFactory<?> protocol = ProtocolFactoryManager.getInstance().getProtocolFactoryByName(profile);
				
		AccordoServizioParteComune as = buildAccordo(properties, bytesInterface, protocol);
		
		addProfileProperties(as, profileProperties);
		
		protocol.createArchive().setProtocolInfo(as);
		
		CleanerOpenSPCoop2Extensions cleaner = new CleanerOpenSPCoop2Extensions();
		cleaner.clean(as);
		
		try(FileOutputStream fout = new FileOutputStream(pathOutputFile)){
			as.writeTo(fout, WriteToSerializerType.XML_JAXB);
		}catch(Exception e) {
			throw new ProtocolException("Serializzazione in '"+pathOutputFile+"' non riuscita: "+e.getMessage(),e);
		}
		
		log.info("Serializzazione completata");
	}
	

	private static String readPathOutpuFile(String [] args, int position) throws ProtocolException {
		String pathOutputFile = args[position];
		if(pathOutputFile==null || StringUtils.isEmpty(pathOutputFile)) {
			throw new ProtocolException("ERROR: argomento 'pathOutputFile' non fornito\n"+USE);
		}
		File fPathOutputFile = new File(pathOutputFile);
		if(fPathOutputFile.exists()) {
			throw new ProtocolException("ERROR: argomento 'pathOutputFile' contiene il riferimento ad un file "+fPathOutputFile.getAbsolutePath()+" già esistente");
		}
		return pathOutputFile;
	}
		
	private static String readProfile(String [] args, int position) throws ProtocolException {
		String profile = args[position];
		if(profile==null || StringUtils.isEmpty(profile)) {
			throw new ProtocolException("ERROR: argomento 'profile' non fornito\n"+USE);
		}
		try {
			ConfigurazionePdD configPdD = new ConfigurazionePdD();
			configPdD.setLoader(new Loader());
			configPdD.setLog(log);
			ProtocolFactoryManager.initialize(log, configPdD, profile);
		}catch(Exception e) {
			throw new ProtocolException("ERROR: argomento 'profile="+profile+"' non valido:"+e.getMessage(),e);
		}
		return profile;
	}
	
	private static byte[] readInterface(String [] args, int position) throws ProtocolException {
		String pathInterface = args[position];
		if(pathInterface==null || StringUtils.isEmpty(pathInterface)) {
			throw new ProtocolException("ERROR: argomento 'pathInterface' non fornito\n"+USE);
		}
		File fPathInterface = new File(pathInterface);
		checkExists(fPathInterface, "pathInterface");
		byte[] bytesInterface = null;
		try {
			bytesInterface = FileSystemUtilities.readBytesFromFile(fPathInterface);
			if(bytesInterface==null || bytesInterface.length<=0) {
				throw new ProtocolException(EMPTY);
			}
		}catch(Exception e) {
			throw new ProtocolException("ERROR: argomento 'pathInterface="+pathInterface+"' non valido:"+e.getMessage(),e);
		}
		return bytesInterface;
	}
	
	private static Properties readProperties(String [] args, int position) throws ProtocolException {
		String pathProperties = args[position];
		if(pathProperties==null || StringUtils.isEmpty(pathProperties)) {
			throw new ProtocolException("ERROR: argomento 'pathProperties' non fornito\n"+USE);
		}
		File fPathProperties = new File(pathProperties);
		checkExists(fPathProperties, "pathProperties");
		byte[] bytesProperties = null;
		try {
			bytesProperties = FileSystemUtilities.readBytesFromFile(fPathProperties);
			if(bytesProperties==null || bytesProperties.length<=0) {
				throw new ProtocolException(EMPTY);
			}
		}catch(Exception e) {
			throw new ProtocolException("ERROR: argomento 'pathProperties="+pathProperties+"' non è un file di proprietà corretto:"+e.getMessage(),e);
		}
		return convertToProperties("pathProperties="+pathProperties,bytesProperties);
	}
	
	private static Properties readProfileProperties(String [] args, int position) throws ProtocolException {
		Properties profileProperties = null;
		if(args.length>=(position+1)) {
			String pathProfileProperties = args[position];
			if(pathProfileProperties!=null && !StringUtils.isEmpty(pathProfileProperties)) {
				File fPathProfileProperties = new File(pathProfileProperties);
				checkExists(fPathProfileProperties, "pathProfileProperties");
				byte[] bytesProfileProperties = null;
				try {
					bytesProfileProperties = FileSystemUtilities.readBytesFromFile(fPathProfileProperties);
					if(bytesProfileProperties==null || bytesProfileProperties.length<=0) {
						throw new ProtocolException(EMPTY);
					}
				}catch(Exception e) {
					throw new ProtocolException("ERROR: argomento 'pathProfileProperties="+pathProfileProperties+"' non è un file di proprietà corretto:"+e.getMessage(),e);
				}
				profileProperties = convertToProperties("pathProfileProperties="+pathProfileProperties,bytesProfileProperties);
			}
		}
		return profileProperties;
	}
	
	private static void checkExists(File f, String name) throws ProtocolException {
		if(!f.exists()) {
			throw new ProtocolException("ERROR: argomento '"+name+"' contiene il riferimento ad un file "+f.getAbsolutePath()+SUFFIX_NON_ESISTENTE);
		}
		if(!f.canRead()) {
			throw new ProtocolException("ERROR: argomento '"+name+"' contiene il riferimento ad un file "+f.getAbsolutePath()+SUFFIX_NON_LEGGIBILE);
		}
	}
	
	private static Properties convertToProperties(String name,byte[]b) throws ProtocolException {
		Properties tmp = new Properties();
		try(ByteArrayInputStream bin = new ByteArrayInputStream(b)){
			tmp.load(bin);
		}catch(Exception e) {
			throw new ProtocolException("ERROR: conversione non  riuscita, argomento '"+name+"' non corretto:"+e.getMessage(),e);
		}
		return tmp;
	}
	
	private static AccordoServizioParteComune buildAccordo(Properties properties, byte[] bytesInterface, IProtocolFactory<?> protocol) throws ProtocolException {
		AccordoServizioParteComune as = new AccordoServizioParteComune();
		as.setNome(readProperty(properties, PROPERTIES_NAME, true));
		as.setDescrizione(readProperty(properties, PROPERTIES_DESCRIPTION, false));
		
		as.setByteWsdlConcettuale(bytesInterface);
		
		as.setProfiloCollaborazione(ProfiloCollaborazione.SINCRONO);
		/**as.setFiltroDuplicati(StatoFunzionalita.DISABILITATO);
		as.setConfermaRicezione(StatoFunzionalita.DISABILITATO);
		as.setConsegnaInOrdine(StatoFunzionalita.DISABILITATO);
		as.setIdCollaborazione(StatoFunzionalita.DISABILITATO);
		as.setIdRiferimentoRichiesta(StatoFunzionalita.DISABILITATO);
		as.setUtilizzoSenzaAzione(true);// default true*/
	
		String serviceBinding = null;
		try {
			serviceBinding = readProperty(properties, PROPERTIES_SERVICE_BINDING, true);
			as.setServiceBinding(ServiceBinding.toEnumConstant(serviceBinding, true));
		}catch(Exception e) {
			throw new ProtocolException("ERROR: 'pathProperties' contiene una proprietà '"+PROPERTIES_SERVICE_BINDING+"' valorizzata con un valore '"+serviceBinding+"' non supportato:"+e.getMessage(),e);
		}
		if(ServiceBinding.SOAP.equals(as.getServiceBinding())) {
			as.setByteWsdlLogicoErogatore(bytesInterface);
		}
		String formatoSpecifica = null;
		try{
			formatoSpecifica = readProperty(properties, PROPERTIES_INTERFACE_TYPE, true);
			as.setFormatoSpecifica(FormatoSpecifica.toEnumConstant(formatoSpecifica, true));
		}catch(Exception e) {
			throw new ProtocolException("ERROR: 'pathProperties' contiene una proprietà '"+PROPERTIES_INTERFACE_TYPE+"' valorizzata con un valore '"+formatoSpecifica+"' non supportato:"+e.getMessage(),e);
		}
		
		String tipoSoggettoDefault = protocol.getManifest().getRegistry().getOrganization().getTypes().getType(0).getName(); 
		IdSoggetto assr = new IdSoggetto();
		assr.setTipo(tipoSoggettoDefault);
		assr.setNome(Costanti.NOME_SOGGETTO_DEFAULT);
		as.setSoggettoReferente(assr);
		
		String versioneApi = null;
		try {
			versioneApi = readProperty(properties, PROPERTIES_VERSION, true);
			as.setVersione(Integer.parseInt(versioneApi));
		}catch(Exception e) {
			throw new ProtocolException("ERROR: argomento 'pathProperties' contiene una proprietà '"+PROPERTIES_VERSION+"' valorizzata con un numero '"+versioneApi+"' che non rappresenta un intero:"+e.getMessage(),e);
		}
		
		String gruppi = readProperty(properties, PROPERTIES_TAG, false);
		if(gruppi!=null && StringUtils.isNotEmpty(gruppi)) {
			List<String> nomiGruppi = Arrays.asList(gruppi.split(","));
			for (String nomeGruppo : nomiGruppi) {
				nomeGruppo = nomeGruppo.trim();
				GruppoAccordo gruppoAccordo = new GruppoAccordo();
				gruppoAccordo.setNome(nomeGruppo);
				if(as.getGruppi()==null) {
					as.setGruppi(new GruppiAccordo());
				}
				as.getGruppi().addGruppo(gruppoAccordo );
			}
		}
		
		String canale = readProperty(properties, PROPERTIES_CANALE, false);
		if(canale!=null && StringUtils.isNotEmpty(canale)) {
			as.setCanale(canale);
		}
		
		return as;
	}

	private static String readProperty(Properties properties, String property, boolean required) throws ProtocolException {
		String v = properties.getProperty(property);
		if(v!=null) {
			v = v.trim();
		}
		if(required && 
				(v==null || StringUtils.isEmpty(v))
			) {
			throw new ProtocolException("Property '"+property+"' not found");
		}
		return v;
	}
	
	private static final String CAST_BOOLEAN = "(boolean) ";
	private static final String CAST_NUMBER = "(number) ";
	private static final String CAST_INT = "(int) ";
	private static final String CAST_LONG = "(long) ";
	private static final String CAST_FILE = "(file) ";
	private static void addProfileProperties(AccordoServizioParteComune as, Properties profileProperties) throws ProtocolException {
		if(profileProperties!=null && !profileProperties.isEmpty()) {
			for (Map.Entry<Object,Object> entry : profileProperties.entrySet()) {
				addProfileProperty(as, entry);
			}
		}
	}
	private static void addProfileProperty(AccordoServizioParteComune as, Map.Entry<Object,Object> entry) throws ProtocolException {
		String key = (String) entry.getKey();
		String value = (String) entry.getValue();
		ProtocolProperty pp = new ProtocolProperty();
		pp.setName(key);
		if(value.startsWith(CAST_BOOLEAN)) {
			pp.setBooleanValue(Boolean.parseBoolean(value.substring(CAST_BOOLEAN.length())));
		}
		else if(value.startsWith(CAST_NUMBER)) {
			pp.setNumberValue(Long.parseLong(value.substring(CAST_NUMBER.length())));
		}
		else if(value.startsWith(CAST_INT)) {
			pp.setNumberValue(Long.parseLong(value.substring(CAST_INT.length())));
		}
		else if(value.startsWith(CAST_LONG)) {
			pp.setNumberValue(Long.parseLong(value.substring(CAST_LONG.length())));
		}
		else if(value.startsWith(CAST_FILE)) {
			String file = value.substring(CAST_FILE.length());
			pp.setFile(file);
			try {
				pp.setByteFile(FileSystemUtilities.readBytesFromFile(file));
			}catch(Exception e) {
				throw new ProtocolException("ERROR: argomento 'pathProfileProperties' contiene una proprietà '"+key+"' valorizzata con un file '"+file+"' non accessibile:"+e.getMessage(),e);
			}
		}
		else {
			pp.setValue(value);
		}
		as.addProtocolProperty(pp);
	}
}