DiagnosticProperties.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.logger;

import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.openspcoop2.utils.Utilities;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.logger.constants.LowSeverity;
import org.openspcoop2.utils.logger.constants.Severity;

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

	private static DiagnosticProperties staticInstance;
	private static synchronized void initialize(Properties diagnosticProperties, boolean throwExceptionPlaceholderFailedResolution) throws UtilsException{
		if(staticInstance==null){
			staticInstance = new DiagnosticProperties(diagnosticProperties, throwExceptionPlaceholderFailedResolution);
		}
	}
	protected static DiagnosticProperties getInstance(Properties diagnosticProperties, boolean throwExceptionPlaceholderFailedResolution) throws UtilsException{
		if(staticInstance==null){
			initialize(diagnosticProperties, throwExceptionPlaceholderFailedResolution);
		}
		return staticInstance;
	}
	
	public static final String EMPTY = "empty";
	
	private static final String PREFIX_FUNCTION = "function.";
	private static final String PREFIX_DIAGNOSTIC = "diagnostic.";
	private static final String SUFFIX_DIAGNOSTIC_CODE = ".code";
	private static final String SUFFIX_DIAGNOSTIC_MESSAGE = ".message";
	private static final String SUFFIX_DIAGNOSTIC_SEVERITY = ".severity";
	
	private static final String DEFAULT_FUNCTION = "default.function";
	
	private static final String DEFAULT_SEVERITY_CODE_PREFIX = "default.severity.";
	
	private Properties diagnosticProperties;
	
	private Map<String, String> mappingFunctionToCode = new HashMap<>();
	private Map<String, String> mappingFullCodeToFullString = new HashMap<>();
	private Map<String, DiagnosticInfo> mappingStringCodeToDiagnostic = new HashMap<String, DiagnosticInfo>(); 
	
	private String defaultFunction;
	private Map<LowSeverity, String> mappingSeverityToCode = new HashMap<LowSeverity, String>();
	
	private boolean throwExceptionPlaceholderFailedResolution;
	
	
	private DiagnosticProperties(Properties diagnosticProperties, boolean throwExceptionPlaceholderFailedResolution) throws UtilsException{
		this.diagnosticProperties = diagnosticProperties;
		
		this.throwExceptionPlaceholderFailedResolution = throwExceptionPlaceholderFailedResolution;
				
		this.init();
	}
	private DiagnosticProperties(){} // for clone
	
	@Override
	public DiagnosticProperties clone(){
		
		DiagnosticProperties dmNew = new DiagnosticProperties();
		
		if(this.defaultFunction!=null){
			dmNew.defaultFunction = new String(this.defaultFunction);
		}
		
		if(this.diagnosticProperties!=null && this.diagnosticProperties.size()>0){
			dmNew.diagnosticProperties = new Properties();
			Enumeration<Object> enKeys = this.diagnosticProperties.keys();
			while (enKeys.hasMoreElements()) {
				Object objKey = (Object) enKeys.nextElement();
				Object objValue = this.diagnosticProperties.get(objKey);	
				dmNew.diagnosticProperties.put(objKey, objValue);
			}
		}
		
		dmNew.throwExceptionPlaceholderFailedResolution = this.throwExceptionPlaceholderFailedResolution;
		
		if(this.mappingFullCodeToFullString!=null && this.mappingFullCodeToFullString.size()>0){
			dmNew.mappingFullCodeToFullString = new HashMap<>();
			for (String key : this.mappingFullCodeToFullString.keySet()) {
				String value = this.mappingFullCodeToFullString.get(key);
				dmNew.mappingFullCodeToFullString.put(key, value);
			}
		}
		
		if(this.mappingFunctionToCode!=null && this.mappingFunctionToCode.size()>0){
			dmNew.mappingFunctionToCode = new HashMap<>();
			for (String key : this.mappingFunctionToCode.keySet()) {
				String value = this.mappingFunctionToCode.get(key);
				dmNew.mappingFunctionToCode.put(key, value);
			}
		}
		
		if(this.mappingSeverityToCode!=null && this.mappingSeverityToCode.size()>0){
			dmNew.mappingSeverityToCode = new HashMap<LowSeverity, String>();
			for (LowSeverity key : this.mappingSeverityToCode.keySet()) {
				String value = this.mappingSeverityToCode.get(key);
				dmNew.mappingSeverityToCode.put(key, value);
			}
		}
		
		if(this.mappingStringCodeToDiagnostic!=null && this.mappingStringCodeToDiagnostic.size()>0){
			dmNew.mappingStringCodeToDiagnostic = new HashMap<String, DiagnosticInfo>();
			for (String key : this.mappingStringCodeToDiagnostic.keySet()) {
				DiagnosticInfo value = this.mappingStringCodeToDiagnostic.get(key);
				dmNew.mappingStringCodeToDiagnostic.put(key, value);
			}
		}
		
		return dmNew;
	}

	
	private void init() throws UtilsException{
		
		if(this.diagnosticProperties.containsKey(EMPTY)){
			return;
		}
		
		// default function
		if(this.diagnosticProperties.containsKey(DEFAULT_FUNCTION)==false){
			throw new UtilsException("Property ["+DEFAULT_FUNCTION+"] undefined");
		}
		this.defaultFunction = this.diagnosticProperties.getProperty(DEFAULT_FUNCTION).trim();
		
		
		// default severity code prefix
		LowSeverity [] s = LowSeverity.values();
		for (int i = 0; i < s.length; i++) {
			String key = DEFAULT_SEVERITY_CODE_PREFIX+s[i].name();
			if(this.diagnosticProperties.containsKey(key)==false){
				throw new UtilsException("Property ["+key+"] undefined");
			}
			this.mappingSeverityToCode.put(s[i], this.diagnosticProperties.getProperty(key).trim());
		}
		
		// Mapping function to code
		Properties functionToCodeProperties = Utilities.readProperties(PREFIX_FUNCTION, this.diagnosticProperties); 
		if(functionToCodeProperties.size()<=0){
			throw new UtilsException("Functions undefined");
		}
		Enumeration<?> enFunction = functionToCodeProperties.keys();
		while (enFunction.hasMoreElements()) {
			String function = (String) enFunction.nextElement();
			String code = functionToCodeProperties.getProperty(function);
			if(this.mappingFunctionToCode.containsKey(function)){
				throw new UtilsException("Function ["+function+"] already exists");
			}
			if(function.contains(".")){
				throw new UtilsException("Function ["+function+"] contain character '.' not permitted");
			}
			if(code==null || code.trim().equals("")){
				throw new UtilsException("Code for function ["+function+"] undefined");
			}
			code = code.trim();
			if(this.mappingFunctionToCode.values().contains(code)){
				throw new UtilsException("Code ["+code+"] already used for other function");
			}
			this.mappingFunctionToCode.put(function, code);
			
			
			// Mapping diagnostic for function
			String diagnosticForFunctionPrefix = PREFIX_DIAGNOSTIC+function+".";
			Properties diagnosticForFunctionProperties = Utilities.readProperties(diagnosticForFunctionPrefix, this.diagnosticProperties);
			if(diagnosticForFunctionProperties.size()<=0){
				throw new UtilsException("Diagnostic for function ["+function+"] undefined");
			}
			Enumeration<?> enDiagnostic = diagnosticForFunctionProperties.keys();
			while (enDiagnostic.hasMoreElements()) {
				
				String diagnosticTmp = (String) enDiagnostic.nextElement();
				if(!diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_CODE) && 
						!diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_MESSAGE) &&
						!diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_SEVERITY)){
					throw new UtilsException("Unexpected element ["+diagnosticForFunctionPrefix+diagnosticTmp+"]");
				}
				String diagnosticNameWithoutSuffix = null;
				if(diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_CODE)){
					diagnosticNameWithoutSuffix = diagnosticTmp.substring(0, diagnosticTmp.length()-SUFFIX_DIAGNOSTIC_CODE.length());
				}
				else if(diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_MESSAGE)){
					diagnosticNameWithoutSuffix = diagnosticTmp.substring(0, diagnosticTmp.length()-SUFFIX_DIAGNOSTIC_MESSAGE.length());
				}
				else if(diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_SEVERITY)){
					diagnosticNameWithoutSuffix = diagnosticTmp.substring(0, diagnosticTmp.length()-SUFFIX_DIAGNOSTIC_SEVERITY.length());
				}
				
				String valueTmp =  diagnosticForFunctionProperties.getProperty(diagnosticTmp);
				if(valueTmp==null || valueTmp.trim().equals("")){
					throw new UtilsException("Value for diagnostic ["+diagnosticForFunctionPrefix+diagnosticTmp+"] undefined");
				}
				valueTmp = valueTmp.trim();
				
				String fullStringCode = function+"."+diagnosticNameWithoutSuffix;
				DiagnosticInfo diagInfo = null;
				if(this.mappingStringCodeToDiagnostic.containsKey(fullStringCode)){
					diagInfo = this.mappingStringCodeToDiagnostic.remove(fullStringCode);
				}
				else{
					diagInfo = new DiagnosticInfo();
					diagInfo.functionCode = code;
					diagInfo.functionString = function;
				}
				if(diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_CODE)){
					if(diagInfo.code!=null){
						throw new UtilsException("Diagnostic ["+diagnosticForFunctionPrefix+diagnosticTmp+"] already defined");
					}
					diagInfo.code = valueTmp;
				}
				else if(diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_MESSAGE)){
					if(diagInfo.message!=null){
						throw new UtilsException("Diagnostic ["+diagnosticForFunctionPrefix+diagnosticTmp+"] already defined");
					}
					diagInfo.message = valueTmp;
				}
				else if(diagnosticTmp.endsWith(SUFFIX_DIAGNOSTIC_SEVERITY)){
					if(diagInfo.severity!=null){
						throw new UtilsException("Diagnostic ["+diagnosticForFunctionPrefix+diagnosticTmp+"] already defined");
					}
					try{
						Severity severity = Severity.valueOf(valueTmp);
						diagInfo.severity = severity;
					}catch(Exception e){
						throw new UtilsException("Value for diagnostic ["+diagnosticForFunctionPrefix+diagnosticTmp+"] is not valid, expected: "+Arrays.asList(Severity.values()));
					}
				}
				this.mappingStringCodeToDiagnostic.put(fullStringCode, diagInfo);
			}
			
		}
		
		
		// Check ogni diagnostico abbia la tripla definita
		for (String stringCode : this.mappingStringCodeToDiagnostic.keySet()) {
			DiagnosticInfo diagInfo = this.mappingStringCodeToDiagnostic.get(stringCode);
			if(diagInfo.code==null){
				throw new UtilsException("Undefined code for diagnostic ["+PREFIX_DIAGNOSTIC+stringCode+"]");
			}
			if(diagInfo.message==null){
				throw new UtilsException("Undefined message for diagnostic ["+PREFIX_DIAGNOSTIC+stringCode+"]");
			}
			if(diagInfo.severity==null){
				throw new UtilsException("Undefined severity for diagnostic ["+PREFIX_DIAGNOSTIC+stringCode+"]");
			}
			//System.out.println("REGISTER ["+diagInfo.functionCode+diagInfo.code+"]["+stringCode+"]");
			this.mappingFullCodeToFullString.put(diagInfo.functionCode+diagInfo.code, stringCode);
		}
	}
	
	protected String getDefaultFunction() {
		return this.defaultFunction;
	}
	
	protected Map<String, String> getMappingFunctionToCode() {
		return this.mappingFunctionToCode;
	}
	protected Map<String, String> getMappingFullCodeToFullString() {
		return this.mappingFullCodeToFullString;
	}
	protected Map<String, DiagnosticInfo> getMappingStringCodeToDiagnostic() {
		return this.mappingStringCodeToDiagnostic;
	}
	protected Map<LowSeverity, String> getMappingSeverityToCode() {
		return this.mappingSeverityToCode;
	}
	protected boolean isThrowExceptionPlaceholderFailedResolution() {
		return this.throwExceptionPlaceholderFailedResolution;
	}
}