All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.mosip.registration.update.SoftwareUpdateHandler Maven / Gradle / Ivy

package io.mosip.registration.update;

import static io.mosip.registration.constants.RegistrationConstants.APPLICATION_ID;
import static io.mosip.registration.constants.RegistrationConstants.APPLICATION_NAME;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.stream.Collectors;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import io.micrometer.core.annotation.Counted;
import io.mosip.kernel.core.logger.spi.Logger;
import io.mosip.kernel.core.util.DateUtils;
import io.mosip.kernel.core.util.FileUtils;
import io.mosip.kernel.logger.logback.util.MetricTag;
import io.mosip.registration.config.AppConfig;
import io.mosip.registration.constants.LoggerConstants;
import io.mosip.registration.constants.RegistrationConstants;
import io.mosip.registration.context.ApplicationContext;
import io.mosip.registration.dto.ResponseDTO;
import io.mosip.registration.exception.RegBaseCheckedException;
import io.mosip.registration.service.BaseService;
import io.mosip.registration.service.config.GlobalParamService;

/**
 * This class will update the application based on comapring the versions of the
 * jars from the Manifest. The comparison will be done by comparing the Local
 * Manifest and the meta-inf.xml file. If there is any updation available in the
 * jar then the new jar gets downloaded and the old gets archived.
 * 
 * @author YASWANTH S
 *
 */
@Component
public class SoftwareUpdateHandler extends BaseService {

	/**
	 * Instance of {@link Logger}
	 */
	private static final Logger LOGGER = AppConfig.getLogger(SoftwareUpdateHandler.class);
	private static final String SLASH = "/";
	private static final String manifestFile = "MANIFEST.MF";
	private static final String libFolder = "lib";
	private static final String dbFolder = "db";
	private static final String binFolder = "bin";
	private static final String lastUpdatedTag = "lastUpdated";
	private static final String SQL = "sql";
	private static final String exectionSqlFile = "initial_db_scripts.sql";
	private static final String rollBackSqlFile = "rollback_scripts.sql";
	private static final String versionTag = "version";
	private static final String MOSIP_SERVICES = "registration-services";
	private static final String MOSIP_CLIENT = "registration-client";
	private static final String FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
	private static final String EXTERNAL_DTD_FEATURE = "http://apache.org/xml/features/nonvalidating/load-external-dtd";

	private static Map CHECKSUM_MAP;
	private String currentVersion;
	private String latestVersion;
	private Manifest localManifest;
	private Manifest serverManifest;
	private String latestVersionReleaseTimestamp;

	@Value("${mosip.reg.rollback.path}")
	private String backUpPath;

	@Value("${mosip.reg.client.url}")
	private String serverRegClientURL;

	@Value("${mosip.reg.xml.file.url}")
	private String serverMosipXmlFileUrl;

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Autowired
	private GlobalParamService globalParamService;

	@Autowired
	private Environment environment;


	public ResponseDTO updateDerbyDB() {
		getCurrentVersion();
		String version = ApplicationContext.getStringValueFromApplicationMap(RegistrationConstants.SERVICES_VERSION_KEY);
		LOGGER.info("Inside updateDerbyDB currentVersion : {} and {} : {}", currentVersion,
				RegistrationConstants.SERVICES_VERSION_KEY, version);
		if(version != null && !version.trim().equals("0") && currentVersion != null && !currentVersion.equalsIgnoreCase(version)) {
			return executeSqlFile(currentVersion, version);
		}
		return null;
	}


	/**
	 * It will check whether any software updates are available or not.
	 * 

* The check will be done by comparing the Local Manifest file version with the * version of the server meta-inf.xml file *

* * @return Boolean true - If there is any update available. false - If no * updates available */ @Counted(recordFailuresOnly = true) public boolean hasUpdate() { LOGGER.info("Checking for any new updates"); try { return !getCurrentVersion().equals(getLatestVersion()); } catch (Throwable exception) { LOGGER.error("Failed to check if update is available or not", exception); return false; } } /** * * @return Returns the current version which is read from the server meta-inf * file. * @throws IOException * @throws ParserConfigurationException * @throws SAXException */ private String getLatestVersion() throws IOException, ParserConfigurationException, SAXException, RegBaseCheckedException { LOGGER.info("Checking for latest version started"); // Get latest version using meta-inf.xml DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setFeature(FEATURE, true); documentBuilderFactory.setFeature(EXTERNAL_DTD_FEATURE, false); documentBuilderFactory.setXIncludeAware(false); documentBuilderFactory.setExpandEntityReferences(false); DocumentBuilder db = documentBuilderFactory.newDocumentBuilder(); try(InputStream in = SoftwareUpdateUtil.download(getURL(serverMosipXmlFileUrl))) { org.w3c.dom.Document metaInfXmlDocument = db.parse(in); setLatestVersion(getElementValue(metaInfXmlDocument, versionTag)); setLatestVersionReleaseTimestamp(getElementValue(metaInfXmlDocument, lastUpdatedTag)); } LOGGER.info("Checking for latest version completed"); return latestVersion; } private String getElementValue(Document metaInfXmlDocument, String tagName) { NodeList list = metaInfXmlDocument.getDocumentElement().getElementsByTagName(tagName); String val = null; if (list != null && list.getLength() > 0) { NodeList subList = list.item(0).getChildNodes(); if (subList != null && subList.getLength() > 0) { // Set Latest Version val = subList.item(0).getNodeValue(); } } return val; } /** * Get Current version of setup * * @return current version */ public String getCurrentVersion() { LOGGER.info("Checking for current version started..."); // Get Local manifest file try { if (getLocalManifest() != null) { setCurrentVersion((String) localManifest.getMainAttributes().get(Attributes.Name.MANIFEST_VERSION)); } } catch (RegBaseCheckedException exception) { LOGGER.error(exception.getMessage(), exception); } LOGGER.info("Checking for current version completed : {}", currentVersion); return currentVersion; } public void doSoftwareUpgrade() { LOGGER.info("Updating latest version started"); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); String date = timestamp.toString().replace(":", "-") + "Z"; File backupFolder = new File(backUpPath + SLASH + getCurrentVersion() + "_" + date); try { // Back Current Application backUpSetup(backupFolder); update(); LOGGER.info("Updating to latest version completed."); return; } catch (Throwable t) { LOGGER.error("Failed with software upgrade", t); } try { rollBackSetup(backupFolder); } catch (io.mosip.kernel.core.exception.IOException e) { LOGGER.error("Failed to rollback setup", e); } } /** *

* Checks whteher the update is available or not *

*

* If the Update is available: *

*

* If the jars needs to be added/updated in the local *

*
    *
  • Take the back-up of the current jars
  • *
  • Download the jars from the server and add/update it in the local
  • *
*

* If the jars needs to be deleted in the local *

*
    *
  • Take the back-up of the current jars
  • *
  • Delete that particular jar from the local
  • *
*

* If there is any error occurs while updation then the restoration of the jars * will happen by taking the back-up jars *

* * @throws Exception * - IOException */ @Counted(recordFailuresOnly = true) private void update() throws Exception { // fetch server manifest && replace local manifest with Server manifest setServerManifest(); serverManifest.write(new FileOutputStream(manifestFile)); setLocalManifest(); SoftwareUpdateUtil.clearTempDirectory(); Map localAttributes = localManifest.getEntries(); for (Map.Entry entry : localAttributes.entrySet()) { File file = new File(libFolder + SLASH + entry.getKey()); String url = serverRegClientURL + latestVersion + SLASH + libFolder + SLASH + entry.getKey(); if(!file.exists()) { LOGGER.info("{} file doesn't exists, downloading it", entry.getKey()); SoftwareUpdateUtil.download(url, entry.getKey()); LOGGER.info("Successfully downloaded the file : {}", entry.getKey()); continue; } if(!SoftwareUpdateUtil.validateJarChecksum(file, entry.getValue())) { LOGGER.info("{} file checksum validation failed, downloading it", entry.getKey()); SoftwareUpdateUtil.download(url, entry.getKey()); LOGGER.info("Successfully downloaded the latest file : {}", entry.getKey()); } } setServerManifest(null); setLatestVersion(null); // Update global param of software update flag as false globalParamService.update(RegistrationConstants.IS_SOFTWARE_UPDATE_AVAILABLE, RegistrationConstants.DISABLE); globalParamService.update(RegistrationConstants.LAST_SOFTWARE_UPDATE, String.valueOf(Timestamp.valueOf(DateUtils.getUTCCurrentDateTime()))); } private void backUpSetup(File backUpFolder) throws io.mosip.kernel.core.exception.IOException { LOGGER.info("Backup of current version started {}", backUpFolder); // bin backup folder File bin = new File(backUpFolder.getAbsolutePath() + SLASH + binFolder); bin.mkdirs(); // lib backup folder File lib = new File(backUpFolder.getAbsolutePath() + SLASH + libFolder); lib.mkdirs(); // db backup folder File db = new File(backUpFolder.getAbsolutePath() + SLASH + dbFolder); db.mkdirs(); // manifest backup file File manifest = new File(backUpFolder.getAbsolutePath() + SLASH + manifestFile); FileUtils.copyDirectory(new File(binFolder), bin); FileUtils.copyDirectory(new File(libFolder), lib); FileUtils.copyDirectory(new File(dbFolder), db); FileUtils.copyFile(new File(manifestFile), manifest); for (File backUpFile : new File(backUpPath).listFiles()) { if (!backUpFile.getAbsolutePath().equals(backUpFolder.getAbsolutePath())) { FileUtils.deleteDirectory(backUpFile); } } globalParamService.update(RegistrationConstants.SOFTWARE_BACKUP_FOLDER, backUpFolder.getAbsolutePath()); LOGGER.info("Backup of current version completed at {}", backUpFolder.getAbsolutePath()); } private void setLocalManifest() throws RegBaseCheckedException { try { File localManifestFile = new File(manifestFile); if (localManifestFile.exists()) { localManifest = new Manifest(new FileInputStream(localManifestFile)); } } catch (IOException e) { LOGGER.error("Failed to load local manifest file", e); throw new RegBaseCheckedException("REG-BUILD-003", "Local Manifest not found"); } } private void setServerManifest() { String url = serverRegClientURL + latestVersion + SLASH + manifestFile; try(InputStream in = SoftwareUpdateUtil.download(url)) { serverManifest = new Manifest(in); } catch (IOException | RegBaseCheckedException e) { LOGGER.error("Failed to load server manifest file", e); } } /** * The latest version timestamp will be taken from the server meta-inf.xml file. * This timestamp will the be parsed in this method. * * @return timestamp */ public Timestamp getLatestVersionReleaseTimestamp() { Calendar calendar = Calendar.getInstance(); String dateString = latestVersionReleaseTimestamp; int year = Integer.valueOf(dateString.charAt(0) + "" + dateString.charAt(1) + "" + dateString.charAt(2) + "" + dateString.charAt(3)); int month = Integer.valueOf(dateString.charAt(4) + "" + dateString.charAt(5)); int date = Integer.valueOf(dateString.charAt(6) + "" + dateString.charAt(7)); int hourOfDay = Integer.valueOf(dateString.charAt(8) + "" + dateString.charAt(9)); int minute = Integer.valueOf(dateString.charAt(10) + "" + dateString.charAt(11)); int second = Integer.valueOf(dateString.charAt(12) + "" + dateString.charAt(13)); calendar.set(year, month - 1, date, hourOfDay, minute, second); return new Timestamp(calendar.getTime().getTime()); } /** * This method will check whether any updation needs to be done in the DB * structure. *

* If there is any updates available: *

*

* Take the back-up of the current DB *

*

* Run the Update queries from the sql file, which is downloaded from the server * and available in the local *

*

* If there is any error occurs during the update,then the rollback query will * run from the sql file *

* * @param actualLatestVersion * latest version * @param previousVersion * previous version * @return response of sql execution * @throws IOException */ @Counted(recordFailuresOnly = true) public ResponseDTO executeSqlFile(@MetricTag("newversion") String actualLatestVersion, @MetricTag("oldversion") String previousVersion) { LOGGER.info("DB-Script files execution started from previous version : {} , To Current Version : {}",previousVersion, currentVersion); String newVersion = actualLatestVersion.split("-")[0]; previousVersion = previousVersion.split("-")[0]; ResponseDTO responseDTO = new ResponseDTO(); try { LOGGER.info("Checking Started : " + newVersion + SLASH + exectionSqlFile); execute(SQL + SLASH + newVersion + SLASH + exectionSqlFile); LOGGER.info("Checking completed : " + newVersion + SLASH + exectionSqlFile); // Update global param with current version globalParamService.update(RegistrationConstants.SERVICES_VERSION_KEY, actualLatestVersion); setSuccessResponse(responseDTO, RegistrationConstants.SQL_EXECUTION_SUCCESS, null); LOGGER.info("DB-Script files execution completed"); return responseDTO; } catch (Throwable exception) { LOGGER.error("Failed to execute db upgrade scripts", exception); } // ROLL BACK QUERIES try { LOGGER.info("Rollback started : " + newVersion + SLASH + rollBackSqlFile); execute(SQL + SLASH + newVersion + SLASH + rollBackSqlFile); LOGGER.info("Rollback completed : " + newVersion + SLASH + rollBackSqlFile); String backupPath = ApplicationContext.getStringValueFromApplicationMap(RegistrationConstants.SOFTWARE_BACKUP_FOLDER); if(backupPath != null) { rollBackSetup(new File(backupPath)); globalParamService.update(RegistrationConstants.SOFTWARE_BACKUP_FOLDER, null); } } catch (Throwable exception) { LOGGER.error("Failed to execute db rollback scripts", exception); } setErrorResponse(responseDTO, RegistrationConstants.SQL_EXECUTION_FAILURE, null); return responseDTO; } private void execute(String path) throws IOException { try (InputStream inputStream = SoftwareUpdateHandler.class.getClassLoader().getResourceAsStream(path)) { LOGGER.info(LoggerConstants.LOG_REG_UPDATE, APPLICATION_NAME, APPLICATION_ID, inputStream != null ? path + " found" : path + " Not Found"); if (inputStream != null) { runSqlFile(inputStream); } } } private void runSqlFile(InputStream inputStream) throws IOException { LOGGER.info(LoggerConstants.LOG_REG_UPDATE, APPLICATION_NAME, APPLICATION_ID, "Execution started sql file"); try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream)) { try (BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { String str; StringBuilder sb = new StringBuilder(); while ((str = bufferedReader.readLine()) != null) { sb.append(str + "\n "); } List statments = java.util.Arrays.asList(sb.toString().split(";")); for (String stat : statments) { if (!stat.trim().equals("")) { LOGGER.info(LoggerConstants.LOG_REG_UPDATE, APPLICATION_NAME, APPLICATION_ID, "Executing Statment : " + stat); jdbcTemplate.execute(stat); } } } } LOGGER.info(LoggerConstants.LOG_REG_UPDATE, APPLICATION_NAME, APPLICATION_ID, "Execution completed sql file"); } private void rollBackSetup(File backUpFolder) throws io.mosip.kernel.core.exception.IOException { LOGGER.info("Replacing Backup of current version started"); if(backUpFolder.exists()) { FileUtils.copyDirectory(new File(backUpFolder.getAbsolutePath() + SLASH + binFolder), new File(binFolder)); FileUtils.copyDirectory(new File(backUpFolder.getAbsolutePath() + SLASH + libFolder), new File(libFolder)); FileUtils.copyFile(new File(backUpFolder.getAbsolutePath() + SLASH + manifestFile), new File(manifestFile)); } LOGGER.info("Replacing Backup of current version completed"); } public Map getJarChecksum() { Map checksumMap = new HashMap<>(); if(localManifest != null) { Map localEntries = localManifest.getEntries(); List keys = localEntries.keySet().stream().filter( k -> k.contains(MOSIP_CLIENT) || k.contains(MOSIP_SERVICES)).collect(Collectors.toList()); for(String key : keys) { checksumMap.put(key, localEntries.get(key).getValue(Attributes.Name.CONTENT_TYPE)); } } return checksumMap; } private String getURL(String urlPostFix) { String upgradeServerURL = ApplicationContext.getStringValueFromApplicationMap(RegistrationConstants.MOSIP_UPGRADE_SERVER_URL); String url = String.format(urlPostFix, upgradeServerURL); url = serviceDelegateUtil.prepareURLByHostName(url); LOGGER.info("Upgrade server : {}", url); return url; } private void setServerManifest(Manifest serverManifest) { this.serverManifest = serverManifest; } private void setCurrentVersion(String currentVersion) { this.currentVersion = currentVersion; } private void setLatestVersion(String latestVersion) { this.latestVersion = latestVersion; } public void setLatestVersionReleaseTimestamp(String latestVersionReleaseTimestamp) { this.latestVersionReleaseTimestamp = latestVersionReleaseTimestamp; } private Manifest getLocalManifest() throws RegBaseCheckedException { if(localManifest == null) { setLocalManifest(); } return localManifest; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy