Advertisement
Guest User

Untitled

a guest
Mar 29th, 2017
94
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 77.16 KB | None | 0 0
  1. /*
  2. * Copyright (c) 2016 Tecom LLC
  3. * All rights reserved
  4. *
  5. * Исключительное право (c) 2016 принадлежит ООО Теком
  6. * Все права защищены
  7. */
  8. package com.tecomgroup.energetics.server.database.migration;
  9.  
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.io.InputStreamReader;
  13. import java.net.MalformedURLException;
  14. import java.sql.Connection;
  15. import java.sql.DriverManager;
  16. import java.sql.PreparedStatement;
  17. import java.sql.ResultSet;
  18. import java.sql.SQLException;
  19. import java.sql.Statement;
  20. import java.util.ArrayList;
  21. import java.util.Arrays;
  22. import java.util.Collections;
  23. import java.util.Date;
  24. import java.util.HashMap;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.NoSuchElementException;
  28. import java.util.Properties;
  29.  
  30. import org.apache.commons.io.IOUtils;
  31. import org.flywaydb.core.Flyway;
  32. import org.flywaydb.core.api.FlywayException;
  33. import org.flywaydb.core.api.MigrationInfo;
  34. import org.flywaydb.core.api.MigrationInfoService;
  35. import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor;
  36. import org.slf4j.Logger;
  37. import org.slf4j.LoggerFactory;
  38.  
  39. import com.tecomgroup.energetics.server.database.domain.TEM_Agent;
  40. import com.tecomgroup.energetics.server.database.utils.AbstractPlaceholderResolver;
  41. import com.tecomgroup.energetics.server.database.utils.AgentProperties;
  42. import com.tecomgroup.energetics.server.database.utils.Utils;
  43. import com.tecomgroup.energetics.server.system.AgentClassLoader;
  44. import com.tecomgroup.energetics.server.system.Constants;
  45.  
  46. /**
  47.  * Allows to manage the database, i.e. create schema,
  48.  * migrate to the latest version, install agent, get
  49.  * the actual version, validate and so on.
  50.  *
  51.  * @author ariskin.a
  52.  */
  53. public class Database {
  54.  
  55.     private static class AgentDirectoryPlaceholderResolver extends AbstractPlaceholderResolver {
  56.    
  57.         private static String homeDir;
  58.        
  59.         static {
  60.             homeDir = System.getenv(Constants.NMS_HOME);
  61.         }
  62.        
  63.         @Override
  64.         protected String resolvePlaceholder(String placeholderName) {
  65.             if (Constants.NMS_HOME.equals(placeholderName)) {
  66.                 return homeDir;
  67.             }
  68.             else {
  69.                 return null;
  70.             }
  71.         }
  72.        
  73.     }
  74.    
  75.     private static class AgentPlaceholderResolver extends AbstractPlaceholderResolver {
  76.  
  77.         private final static String AGENT_ID_PLACEHOLDER = "agent_id";
  78.         private final static String AGENT_NAME_PLACEHOLDER = "agent_name";
  79.        
  80.         private TEM_Agent agent;
  81.        
  82.         public AgentPlaceholderResolver(TEM_Agent agent) {
  83.             this.agent = agent;
  84.         }
  85.        
  86.         @Override
  87.         protected String resolvePlaceholder(String placeholderName) {
  88.             if (AGENT_ID_PLACEHOLDER.equals(placeholderName)) {
  89.                 return agent.getId().toString();
  90.             }
  91.             else if (AGENT_NAME_PLACEHOLDER.equals(placeholderName)) {
  92.                 return agent.getName().toString();
  93.             }
  94.             else {
  95.                 return null;
  96.             }
  97.         }
  98.        
  99.     }
  100.    
  101.     private static final Logger LOGGER = LoggerFactory.getLogger(Database.class);
  102.    
  103.     private static final String[] CREATE_SCRIPTS = new String[] {
  104.         "schema_create_pre.sql",
  105.         "schema_create.sql",
  106.         "schema_create_post.sql"
  107.     };
  108.  
  109.     private static final String[] DROP_SCRIPTS = new String[] {
  110.         "schema_drop_pre.sql",
  111.         "schema_drop.sql",
  112.         "schema_drop_post.sql"
  113.     };
  114.    
  115.     private static final String IMPORT_SCRIPT = "import.sql";
  116.  
  117.     private static final String RESOURCE_FOLDER_SQL_CORE = "sql";
  118.  
  119.     private static final String CORE_SQL_MIGRATIONS_PREFIX = "migration";
  120.     private static final String CORE_JAVA_MIGRATIONS_PACKAGE = "com.tecomgroup.energetics.server.database.migration";
  121.     private static final String COMMON_SQL_MIGRATIONS_FOLDER = "common";
  122.     private static final String PLACEHOLDER_PREFIX = "$${";
  123.     //private static final String SCHEMA_VERSION_CORE_TABLE = "schema_version";
  124.     private static final String SCHEMA_VERSION_AGENT_TABLE_TEMPLATE = "schema_version_%s";
  125.    
  126.     private final DatabaseConfiguration databaseConfig;
  127.    
  128.     public Database(DatabaseConfiguration databaseConfig) {
  129.         this.databaseConfig = databaseConfig;
  130.     }
  131.    
  132.     public static void createSchema(DatabaseConfiguration databaseConfig, String[] agentJarPaths) throws Exception {
  133.         Database database = new Database(databaseConfig);
  134.         database.createSchema(agentJarPaths);
  135.     }
  136.    
  137.     public void createSchema(String[] agentJarPaths) throws Exception {
  138.         Connection connection = getConnection();
  139.         try {
  140.             AgentClassLoader classLoader = getAgentClassLoaderByJars(agentJarPaths);
  141.             try {
  142.                 createSchema(classLoader, connection);
  143.             }
  144.             finally {
  145.                 classLoader.close();
  146.             }
  147.         }
  148.         finally {
  149.             connection.close();
  150.         }
  151.     }
  152.    
  153.     private void createSchema(AgentClassLoader classLoader, Connection connection) throws Exception {
  154.         LOGGER.info("Creating database schema...");
  155.        
  156.         String dialectFolder = Utils.getFolderByDialect(databaseConfig.getDialect());
  157.         LOGGER.debug("Dialect folder: {}", dialectFolder);
  158.        
  159.         Class<? extends ImportSqlCommandExtractor> commandExtractorClass = getCommandExtractorClassOrDefault();
  160.         LOGGER.debug("Command extractor class: {}", commandExtractorClass.getCanonicalName());
  161.         ImportSqlCommandExtractor commandExtractor = commandExtractorClass.newInstance();
  162.        
  163.         String encoding = getEncodingOrDefault();
  164.         LOGGER.debug("Encoding: {}", encoding);
  165.        
  166.         createSchemaCore(classLoader, connection, dialectFolder, commandExtractor, encoding);
  167.        
  168.         Flyway flywayCore = createFlywayCore(classLoader);
  169.         internalBaselineToLatestVersion(flywayCore);
  170.        
  171.         for (String agentName : classLoader.getAgentNames()) {
  172.             installAgent(classLoader, connection, agentName, dialectFolder, commandExtractor, encoding);
  173.         }
  174.        
  175.         LOGGER.info("Database schema created");
  176.     }
  177.    
  178.     private void createSchemaCore(AgentClassLoader classLoader, Connection connection, String dialectFolder, ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  179.         LOGGER.info("Creating schema for core...");
  180.         String resourceFolder = RESOURCE_FOLDER_SQL_CORE;
  181.         internalCreateSchema(classLoader, connection, resourceFolder, dialectFolder, commandExtractor, encoding);
  182.     }
  183.    
  184.     private void createSchemaAgent(AgentClassLoader classLoader, Connection connection, TEM_Agent agent, String dialectFolder, ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  185.         LOGGER.info("Creating schema for agent {}...", agent.getName());
  186.         String resourceFolder = getAgentSqlResourceFolder(classLoader, agent.getName());
  187.         internalCreateSchema(classLoader, connection, resourceFolder, dialectFolder, commandExtractor, encoding, new AgentPlaceholderResolver(agent));
  188.     }
  189.    
  190.     private void internalCreateSchema(AgentClassLoader classLoader, Connection connection, String resourceFolder, String dialectFolder,
  191.             ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  192.         internalCreateSchema(classLoader, connection, resourceFolder, dialectFolder, commandExtractor, encoding, null);
  193.     }
  194.    
  195.     private void internalCreateSchema(AgentClassLoader classLoader, Connection connection, String resourceFolder, String dialectFolder,
  196.             ImportSqlCommandExtractor commandExtractor, String encoding, AbstractPlaceholderResolver placeholderResolver) throws Exception {
  197.         LOGGER.debug("Creating schema, resource folder: {}, dialect folder: {}", resourceFolder, dialectFolder);
  198.        
  199.         LOGGER.debug("Loading create commands...");
  200.         String[] createCommands = loadCommands(classLoader, commandExtractor, resourceFolder, dialectFolder, CREATE_SCRIPTS, encoding);
  201.         if (placeholderResolver != null) {
  202.             LOGGER.debug("Using placeholder resolver {}...", placeholderResolver.getClass().getCanonicalName());
  203.             for (int i = 0; i < createCommands.length; ++i) {
  204.                 String createCommand = createCommands[i];
  205.                 String replacedCreateCommand = placeholderResolver.replacePlaceholders(createCommand);
  206.                 if (!replacedCreateCommand.equals(createCommand)) {
  207.                     LOGGER.trace("Create command: '{}' -> '{}'", createCommand, replacedCreateCommand);
  208.                 }
  209.                 createCommands[i] = replacedCreateCommand;
  210.             }
  211.         }
  212.         if (LOGGER.isTraceEnabled()) {
  213.             LOGGER.trace("Create commands:");
  214.             for (String createCommand : createCommands) {
  215.                 LOGGER.trace("{}", createCommand);
  216.             }
  217.         }
  218.        
  219.         LOGGER.info("Executing create commands...");
  220.         List<Exception> createExceptions = executeCommands(connection, createCommands);
  221.         if (createExceptions.size() > 0) {
  222.             LOGGER.error("Exceptions during execution of create scripts:");
  223.             for (Exception ex : createExceptions) {
  224.                 LOGGER.error("Exception", ex);
  225.             }
  226.             throw new Exception("Failed to execute schema create scripts");
  227.         }
  228.         LOGGER.info("Create commands executed");
  229.  
  230.         LOGGER.debug("Loading import script...");
  231.         String importScript = loadScriptAsString(classLoader, resourceFolder, dialectFolder, IMPORT_SCRIPT, encoding);
  232.         if (importScript != null) {
  233.             if (placeholderResolver != null) {
  234.                 LOGGER.debug("Using placeholder resolver {}...", placeholderResolver.getClass().getCanonicalName());
  235.                 String replacedImportScript = placeholderResolver.replacePlaceholders(importScript);
  236.                 if (!replacedImportScript.equals(importScript)) {
  237.                     LOGGER.trace("Import script: '{}' -> '{}", importScript, replacedImportScript);
  238.                 }
  239.                 importScript = replacedImportScript;
  240.             }
  241.             LOGGER.trace("Import script: {}", importScript);
  242.            
  243.             LOGGER.info("Executing import script...");
  244.             try {
  245.                 if (!importScript.trim().isEmpty()) {
  246.                     executeScript(connection, importScript);
  247.                 }
  248.                 else {
  249.                     LOGGER.debug("Import script is empty");
  250.                 }
  251.             }
  252.             catch (SQLException ex) {
  253.                 throw new Exception("Failed to execute import script", ex);
  254.             }
  255.             LOGGER.info("Import script executed");
  256.         }
  257.     }
  258.  
  259.     public static void dropSchema(DatabaseConfiguration databaseConfig) throws Exception {
  260.         Database database = new Database(databaseConfig);
  261.         database.dropSchema();
  262.     }
  263.  
  264.     public void dropSchema() throws Exception {
  265.         Connection connection = getConnection();
  266.         try {
  267.             AgentClassLoader classLoader = getAgentClassLoaderByInstalledAgents(connection);
  268.             try {
  269.                 dropSchema(classLoader, connection);
  270.             }
  271.             finally {
  272.                 classLoader.close();
  273.             }
  274.         }
  275.         finally {
  276.             connection.close();
  277.         }
  278.     }
  279.    
  280.     private void dropSchema(AgentClassLoader classLoader, Connection connection) throws Exception {
  281.         LOGGER.info("Dropping database schema...");
  282.        
  283.         String dialectFolder = Utils.getFolderByDialect(databaseConfig.getDialect());
  284.         LOGGER.debug("Dialect folder: {}", dialectFolder);
  285.        
  286.         Class<? extends ImportSqlCommandExtractor> commandExtractorClass = getCommandExtractorClassOrDefault();
  287.         LOGGER.debug("Command extractor class: {}", commandExtractorClass.getCanonicalName());
  288.         ImportSqlCommandExtractor commandExtractor = commandExtractorClass.newInstance();
  289.        
  290.         String encoding = getEncodingOrDefault();
  291.         LOGGER.debug("Encoding: {}", encoding);
  292.        
  293.         List<TEM_Agent> installedAgents = getInstalledAgents(connection);
  294.         for (TEM_Agent installedAgent : installedAgents) {
  295.             uninstallAgent(classLoader, connection, installedAgent, dialectFolder, commandExtractor, encoding);
  296.         }
  297.  
  298.         dropSchemaCore(classLoader, connection, dialectFolder, commandExtractor, encoding);
  299.        
  300.         LOGGER.info("Database schema dropped");
  301.     }
  302.    
  303.     private void dropSchemaCore(AgentClassLoader classLoader, Connection connection, String dialectFolder, ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  304.         LOGGER.info("Dropping schema for core...");
  305.         String resourceFolder = RESOURCE_FOLDER_SQL_CORE;
  306.         internalDropSchema(classLoader, connection, resourceFolder, dialectFolder, commandExtractor, encoding);
  307.     }
  308.    
  309.     private void dropSchemaAgent(AgentClassLoader classLoader, Connection connection, final TEM_Agent agent, String dialectFolder, ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  310.         LOGGER.info("Dropping schema for agent {}...", agent.getName());
  311.         String resourceFolder = getAgentSqlResourceFolder(classLoader, agent.getName());
  312.         internalDropSchema(classLoader, connection, resourceFolder, dialectFolder, commandExtractor, encoding, new AgentPlaceholderResolver(agent));
  313.     }
  314.    
  315.     private void internalDropSchema(AgentClassLoader classLoader, Connection connection, String resourceFolder, String dialectFolder,
  316.             ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  317.         internalDropSchema(classLoader, connection, resourceFolder, dialectFolder, commandExtractor, encoding, null);
  318.     }
  319.    
  320.     private void internalDropSchema(AgentClassLoader classLoader, Connection connection, String resourceFolder, String dialectFolder,
  321.             ImportSqlCommandExtractor commandExtractor, String encoding, AbstractPlaceholderResolver placeholderResolver) throws Exception {
  322.         LOGGER.debug("Dropping schema, resource folder: {}, dialect folder: {}", resourceFolder, dialectFolder);
  323.        
  324.         LOGGER.debug("Loading drop commands...");
  325.         String[] dropCommands = loadCommands(classLoader, commandExtractor, resourceFolder, dialectFolder, DROP_SCRIPTS, encoding);
  326.         if (placeholderResolver != null) {
  327.             LOGGER.debug("Using placeholder resolver {}...", placeholderResolver.getClass().getCanonicalName());
  328.             for (int i = 0; i < dropCommands.length; ++i) {
  329.                 String dropCommand = dropCommands[i];
  330.                 String replacedDropCommand = placeholderResolver.replacePlaceholders(dropCommand);
  331.                 if (!replacedDropCommand.equals(dropCommand)) {
  332.                     LOGGER.trace("Create command: '{}' -> '{}'", dropCommand, replacedDropCommand);
  333.                 }
  334.                 dropCommands[i] = replacedDropCommand;
  335.             }
  336.         }
  337.         if (LOGGER.isTraceEnabled()) {
  338.             LOGGER.trace("Drop commands:");
  339.             for (String dropCommand : dropCommands) {
  340.                 LOGGER.trace("{}", dropCommand);
  341.             }
  342.         }
  343.        
  344.         LOGGER.info("Executing drop commands...");
  345.         List<Exception> dropExceptions = executeCommands(connection, dropCommands);
  346.         LOGGER.info("Drop commands executed");
  347.         if (LOGGER.isDebugEnabled()) {
  348.             for (Exception dropException : dropExceptions) {
  349.                 LOGGER.debug("Drop exception", dropException);
  350.             }
  351.         }
  352.        
  353.         /*
  354.         try {
  355.             dropCoreSchemaVersionTable(connection);
  356.         }
  357.         catch (SQLException ex) {
  358.             LOGGER.debug("Failed to drop core schema version table", ex);
  359.         }
  360.         */
  361.     }
  362.  
  363.     public static void upgradeSchema(DatabaseConfiguration databaseConfig, String[] agentJarPaths) throws Exception {
  364.         Database database = new Database(databaseConfig);
  365.         database.upgradeSchema(agentJarPaths);
  366.     }
  367.    
  368.     public void upgradeSchema(String[] agentJarPaths) throws Exception {
  369.         Connection connection = getConnection();
  370.         try {
  371.             AgentClassLoader classLoader = getAgentClassLoaderByJars(agentJarPaths);
  372.             try {
  373.                 upgradeSchema(classLoader, connection);
  374.             }
  375.             finally {
  376.                 classLoader.close();
  377.             }
  378.         }
  379.         finally {
  380.             connection.close();
  381.         }
  382.     }
  383.    
  384.     private void upgradeSchema(AgentClassLoader classLoader, Connection connection) throws Exception {
  385.         LOGGER.info("Upgrading database schema...");
  386.        
  387.         String dialectFolder = Utils.getFolderByDialect(databaseConfig.getDialect());
  388.         LOGGER.debug("Dialect folder: {}", dialectFolder);
  389.        
  390.         Class<? extends ImportSqlCommandExtractor> commandExtractorClass = getCommandExtractorClassOrDefault();
  391.         LOGGER.debug("Command extractor class: {}", commandExtractorClass.getCanonicalName());
  392.         ImportSqlCommandExtractor commandExtractor = commandExtractorClass.newInstance();
  393.        
  394.         String encoding = getEncodingOrDefault();
  395.         LOGGER.debug("Encoding: {}", encoding);
  396.        
  397.         Flyway flywayCore = createFlywayCore(classLoader);
  398.         migrateSchema(flywayCore);
  399.        
  400.         for (String agentName : classLoader.getAgentNames()) {
  401.             installOrUpgradeAgent(classLoader, connection, agentName, dialectFolder, commandExtractor, encoding);
  402.         }
  403.  
  404.         LOGGER.info("Database schema upgraded");
  405.     }
  406.    
  407.     public static void installAgent(DatabaseConfiguration databaseConfig, String agentJarPath) throws Exception {
  408.         Database database = new Database(databaseConfig);
  409.         database.installAgent(agentJarPath);
  410.     }
  411.    
  412.     public void installAgent(String agentJarPath) throws Exception {
  413.         Connection connection = getConnection();
  414.         try {
  415.             AgentClassLoader classLoader = getAgentClassLoaderByJar(agentJarPath);
  416.             try {
  417.                 String agentName;
  418.                 try {
  419.                     agentName = classLoader.getAgentNames().iterator().next();
  420.                 }
  421.                 catch (NoSuchElementException ex) {
  422.                     String errorMsg = String.format("Failed to get agent name from JAR {}. Probably, you specified not an agent JAR.", agentJarPath);
  423.                     LOGGER.error("{}", errorMsg);
  424.                     throw new Exception(errorMsg);
  425.                 }
  426.                
  427.                 installAgent(classLoader, connection, agentName);
  428.             }
  429.             finally {
  430.                 classLoader.close();
  431.             }
  432.         }
  433.         finally {
  434.             connection.close();
  435.         }
  436.     }
  437.    
  438.     private void installAgent(AgentClassLoader classLoader, Connection connection, String agentName) throws Exception {
  439.         LOGGER.info("Installing agent {}...", agentName);
  440.        
  441.         String dialectFolder = Utils.getFolderByDialect(databaseConfig.getDialect());
  442.         LOGGER.debug("Dialect folder: {}", dialectFolder);
  443.        
  444.         Class<? extends ImportSqlCommandExtractor> commandExtractorClass = getCommandExtractorClassOrDefault();
  445.         LOGGER.debug("Command extractor class: {}", commandExtractorClass.getCanonicalName());
  446.         ImportSqlCommandExtractor commandExtractor = commandExtractorClass.newInstance();
  447.        
  448.         String encoding = getEncodingOrDefault();
  449.         LOGGER.debug("Encoding: {}", encoding);
  450.        
  451.         installAgent(classLoader, connection, agentName, dialectFolder, commandExtractor, encoding);
  452.        
  453.         LOGGER.info("Agent {} installed", agentName);
  454.     }
  455.    
  456.     private void installAgent(AgentClassLoader classLoader, Connection connection, String agentName, String dialectFolder, ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  457.         LOGGER.debug("Installing agent {}...", agentName);
  458.  
  459.         if (!isAgentsTableExists(connection)) {
  460.             throw new Exception("Agents table not exists");
  461.         }
  462.        
  463.         TEM_Agent agent = getInstalledAgentByName(connection, agentName);
  464.         if (agent != null) {
  465.             throw new Exception(String.format("Agent %s is already installed", agentName));
  466.         }
  467.        
  468.         agent = insertAgentIntoAgentsTable(classLoader, connection, agentName, dialectFolder);
  469.        
  470.         createSchemaAgent(classLoader, connection, agent, dialectFolder, commandExtractor, encoding);
  471.        
  472.         createAgentSchemaVersionTable(classLoader, connection, agent.getName());
  473.     }
  474.    
  475.     private TEM_Agent insertAgentIntoAgentsTable(AgentClassLoader classLoader, Connection connection, String agentName, String dialectFolder) throws Exception {
  476.         LOGGER.debug("Inserting agent {} into agents table...", agentName);
  477.        
  478.         Properties agentProperties = getAgentProperties(classLoader, agentName);
  479.         String agentVersion = agentProperties.getProperty(AgentProperties.AGENT_VERSION_PROPERTY);
  480.         LOGGER.debug("Agent version: {}", agentVersion);
  481.         String agentDescription = agentProperties.getProperty(AgentProperties.AGENT_DESCRIPTION_PROPERTY);
  482.         LOGGER.debug("Agent version: {}", agentVersion);
  483.         Class<?> deviceClass = classLoader.getDeviceClassByAgentName(agentName);
  484.         LOGGER.debug("Device class: {}", deviceClass.getCanonicalName());
  485.         String agentDirectory = String.format("${NMS_HOME}/agents/%s", agentName); // assume this directory name is used for an agent
  486.         LOGGER.debug("Agent directory: {}", agentDirectory);
  487.         String agentDisplayName = agentProperties.getProperty(AgentProperties.AGENT_DISPLAY_NAME_PROPERTY);
  488.         LOGGER.debug("Agent display name: {}", agentDisplayName);
  489.         Class<?> agentClass = classLoader.getAgentClassByAgentName(agentName);
  490.         LOGGER.debug("Agent class: {}", agentClass.getCanonicalName());
  491.        
  492.         String insertQuery = getInsertAgentQuery(dialectFolder);
  493.         LOGGER.debug("Insert query: {}", insertQuery);
  494.         PreparedStatement insertAgentStatement = connection.prepareStatement(insertQuery, Statement.RETURN_GENERATED_KEYS);
  495.         insertAgentStatement.setString(1, agentVersion);
  496.         insertAgentStatement.setString(2, agentDescription);
  497.         insertAgentStatement.setString(3, deviceClass.getCanonicalName());
  498.         insertAgentStatement.setString(4, agentDirectory);
  499.         insertAgentStatement.setString(5, agentDisplayName);
  500.         insertAgentStatement.setDate(6, new java.sql.Date(new Date().getTime()));
  501.         insertAgentStatement.setString(7, agentName);
  502.         insertAgentStatement.setString(8, agentClass.getCanonicalName());
  503.        
  504.         int insertedRows = insertAgentStatement.executeUpdate();
  505.         LOGGER.debug("Inserted {} rows", insertedRows);
  506.         if (insertedRows <= 0) {
  507.             throw new Exception("Failed to insert data into agents table");
  508.         }
  509.        
  510.         ResultSet generatedKeys = insertAgentStatement.getGeneratedKeys();
  511.         if (generatedKeys.next()) {
  512.             Long insertedId = generatedKeys.getLong(1);
  513.             LOGGER.debug("Inserted ID: {}", insertedId);
  514.            
  515.             TEM_Agent insertedAgent = getInstalledAgentById(connection, insertedId);
  516.             return insertedAgent;
  517.         }
  518.         else {
  519.             // For unknown reason, we could't obtain ID of the inserted record.
  520.             // So, we try to get agent by its name. This won't work if we implement
  521.             // feature with different agent versions.
  522.             // For now, just warn.
  523.             LOGGER.warn("Failed to get inserted ID.");
  524.            
  525.             TEM_Agent insertedAgent = getInstalledAgentByName(connection, agentName);
  526.             return insertedAgent;
  527.         }
  528.     }
  529.    
  530.     private void createAgentSchemaVersionTable(AgentClassLoader classLoader, Connection connection, String agentName) throws Exception {
  531.         LOGGER.info("Creating schema version table for agent {}...", agentName);
  532.         Flyway flywayAgent = createFlywayAgent(classLoader, agentName);
  533.         internalBaselineToLatestVersion(flywayAgent);
  534.     }
  535.    
  536.     public static void uninstallAgent(DatabaseConfiguration databaseConfig, Long agentId) throws Exception {
  537.         Database database = new Database(databaseConfig);
  538.         database.uninstallAgent(agentId);
  539.     }
  540.    
  541.     public void uninstallAgent(Long agentId) throws Exception {
  542.         Connection connection = getConnection();
  543.         try {
  544.             if (!isAgentsTableExists(connection)) {
  545.                 throw new Exception("Agents table not exists");
  546.             }
  547.  
  548.             TEM_Agent agent = getInstalledAgentById(connection, agentId);
  549.             if (agent == null) {
  550.                 String errorMsg = String.format("Failed to find agent with ID %d", agentId);
  551.                 LOGGER.error(errorMsg);
  552.                 throw new Exception(errorMsg);
  553.             }
  554.  
  555.             AgentClassLoader classLoader = getAgentClassLoaderByDirectory(agent.getDirectory());
  556.             try {
  557.                 uninstallAgent(classLoader, connection, agent);
  558.             }
  559.             finally {
  560.                 classLoader.close();
  561.             }
  562.         }
  563.         finally {
  564.             connection.close();
  565.         }
  566.     }
  567.    
  568.     public static void uninstallAgent(DatabaseConfiguration databaseConfig, String agentName) throws Exception {
  569.         Database database = new Database(databaseConfig);
  570.         database.uninstallAgent(agentName);
  571.     }
  572.    
  573.     public void uninstallAgent(String agentName) throws Exception {
  574.         Connection connection = getConnection();
  575.         try {
  576.             if (!isAgentsTableExists(connection)) {
  577.                 throw new Exception("Agents table not exists");
  578.             }
  579.  
  580.             TEM_Agent agent = getInstalledAgentByName(connection, agentName);
  581.             if (agent == null) {
  582.                 String errorMsg = String.format("Failed to find agent with name %s", agentName);
  583.                 LOGGER.error(errorMsg);
  584.                 throw new Exception(errorMsg);
  585.             }
  586.  
  587.             AgentClassLoader classLoader = getAgentClassLoaderByDirectory(agent.getDirectory());
  588.             try {
  589.                 uninstallAgent(classLoader, connection, agent);
  590.             }
  591.             finally {
  592.                 classLoader.close();
  593.             }
  594.         }
  595.         finally {
  596.             connection.close();
  597.         }
  598.     }
  599.    
  600.     private void uninstallAgent(AgentClassLoader classLoader, Connection connection, TEM_Agent agent) throws Exception {
  601.         LOGGER.info("Uninstalling agent {} (ID {})...", agent.getName(), agent.getId());
  602.        
  603.         String dialectFolder = Utils.getFolderByDialect(databaseConfig.getDialect());
  604.         LOGGER.debug("Dialect folder: {}", dialectFolder);
  605.        
  606.         Class<? extends ImportSqlCommandExtractor> commandExtractorClass = getCommandExtractorClassOrDefault();
  607.         LOGGER.debug("Command extractor class: {}", commandExtractorClass.getCanonicalName());
  608.         ImportSqlCommandExtractor commandExtractor = commandExtractorClass.newInstance();
  609.        
  610.         String encoding = getEncodingOrDefault();
  611.         LOGGER.debug("Encoding: {}", encoding);
  612.        
  613.         uninstallAgent(classLoader, connection, agent, dialectFolder, commandExtractor, encoding);
  614.        
  615.         LOGGER.debug("Agent {} (ID {}) uninstalled", agent.getName(), agent.getId());
  616.     }
  617.    
  618.     private void uninstallAgent(AgentClassLoader classLoader, Connection connection, TEM_Agent agent, String dialectFolder, ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  619.         LOGGER.debug("Uninstalling agent {} (ID {})...", agent.getName(), agent.getId());
  620.  
  621.         try {
  622.             dropAgentSchemaVersionTable(connection, agent.getName());
  623.         }
  624.         catch (SQLException ex) {
  625.             LOGGER.error("Failed to drop schema version table for agent {}", agent.getName(), ex);
  626.         }
  627.        
  628.         dropSchemaAgent(classLoader, connection, agent, dialectFolder, commandExtractor, encoding);
  629.        
  630.         if (isAgentsTableExists(connection)) {
  631.             try {
  632.                 deleteAgentFromAgentsTable(connection, agent.getId());
  633.             }
  634.             catch (SQLException ex) {
  635.                 LOGGER.error("Failed to delete agent from agents table", ex);
  636.             }
  637.         }
  638.     }
  639.    
  640.     /*
  641.     private void dropCoreSchemaVersionTable(Connection connection) throws SQLException {
  642.         LOGGER.debug("Dropping schema version table for core...");
  643.         Statement dropCoreSchemaVersionTableStatement = connection.createStatement();
  644.         String queryDropCoreSchemaVersionTable = String.format("DROP TABLE \"%s\"", SCHEMA_VERSION_CORE_TABLE);
  645.         LOGGER.debug("Drop table SQL statement: {}", queryDropCoreSchemaVersionTable);
  646.         dropCoreSchemaVersionTableStatement.execute(queryDropCoreSchemaVersionTable);
  647.     }
  648.     */
  649.  
  650.     private void dropAgentSchemaVersionTable(Connection connection, String agentName) throws SQLException {
  651.         LOGGER.debug("Dropping schema version table for agent {}...", agentName);
  652.         String agentSchemaVersionTableName = getAgentSchemaVersionTableName(agentName);
  653.         LOGGER.debug("Schema version table name: {}", agentSchemaVersionTableName);
  654.         Statement dropAgentSchemaVersionTableStatement = connection.createStatement();
  655.         String queryDropAgentSchemaVersionTable = String.format("DROP TABLE \"%s\"", agentSchemaVersionTableName);
  656.         LOGGER.debug("Drop table SQL statement: {}", queryDropAgentSchemaVersionTable);
  657.         dropAgentSchemaVersionTableStatement.execute(queryDropAgentSchemaVersionTable);
  658.     }
  659.    
  660.     private static final String queryDeleteAgent = "DELETE FROM tem_agent WHERE id=?";
  661.  
  662.     private void deleteAgentFromAgentsTable(Connection connection, Long agentId) throws SQLException {
  663.         LOGGER.debug("Delete agent with ID {} from agents table...", agentId);
  664.         PreparedStatement deleteAgentStatement = connection.prepareStatement(queryDeleteAgent);
  665.         deleteAgentStatement.setLong(1, agentId);
  666.         deleteAgentStatement.execute();
  667.     }
  668.  
  669.     public static void upgradeAgent(DatabaseConfiguration databaseConfig, String agentJarPath) throws Exception {
  670.         Database database = new Database(databaseConfig);
  671.         database.upgradeAgent(agentJarPath);
  672.     }
  673.    
  674.     public void upgradeAgent(String agentJarPath) throws Exception {
  675.         Connection connection = getConnection();
  676.         try {
  677.             AgentClassLoader classLoader = getAgentClassLoaderByJar(agentJarPath);
  678.             try {
  679.                 String agentName;
  680.                 try {
  681.                     agentName = classLoader.getAgentNames().iterator().next();
  682.                 }
  683.                 catch (NoSuchElementException ex) {
  684.                     String errorMsg = String.format("Failed to get agent name from JAR {}. Probably, you specified not an agent JAR.", agentJarPath);
  685.                     LOGGER.error("{}", errorMsg);
  686.                     throw new Exception(errorMsg);
  687.                 }
  688.  
  689.                 upgradeAgent(classLoader, connection, agentName);
  690.             }
  691.             finally {
  692.                 classLoader.close();
  693.             }
  694.         }
  695.         finally {
  696.             connection.close();
  697.         }
  698.     }
  699.    
  700.     private void upgradeAgent(AgentClassLoader classLoader, Connection connection, String agentName) throws Exception {
  701.         LOGGER.debug("Upgrading agent {}...", agentName);
  702.        
  703.         if (!isAgentsTableExists(connection)) {
  704.             throw new Exception("Agents table not exists");
  705.         }
  706.  
  707.         TEM_Agent agent = getInstalledAgentByName(connection, agentName);
  708.         if (agent == null) {
  709.             throw new Exception(String.format("Agent %s is not installed", agentName));
  710.         }
  711.        
  712.         internalUpgradeAgent(classLoader, connection, agentName);
  713.     }
  714.    
  715.     private void internalUpgradeAgent(AgentClassLoader classLoader, Connection connection, String agentName) throws Exception {
  716.         updateAgentInAgentsTable(classLoader, connection, agentName);
  717.        
  718.         upgradeAgentSchema(classLoader, agentName);
  719.        
  720.         // assume that no changed required in schema version table for agent
  721.     }
  722.    
  723.     private void upgradeAgentSchema(AgentClassLoader classLoader, String agentName) throws Exception {
  724.         LOGGER.debug("Upgrading schema for agent {}...", agentName);
  725.        
  726.         Flyway flywayAgent = createFlywayAgent(classLoader, agentName);
  727.         migrateSchema(flywayAgent);
  728.     }
  729.    
  730.     private static final String queryUpdateAgentByName = "UPDATE tem_agent " +
  731.             "SET agentversion=?, description=?, deviceclass=?, displayname=?, agentclass=? WHERE name=?";
  732.  
  733.     private void updateAgentInAgentsTable(AgentClassLoader classLoader, Connection connection, String agentName) throws Exception {
  734.         LOGGER.debug("Updating agent {} in agents table...", agentName);
  735.  
  736.         Properties agentProperties = getAgentProperties(classLoader, agentName);
  737.         String agentVersion = agentProperties.getProperty(AgentProperties.AGENT_VERSION_PROPERTY);
  738.         LOGGER.debug("Agent version: {}", agentVersion);
  739.         String agentDescription = agentProperties.getProperty(AgentProperties.AGENT_DESCRIPTION_PROPERTY);
  740.         LOGGER.debug("Agent version: {}", agentVersion);
  741.         Class<?> deviceClass = classLoader.getDeviceClassByAgentName(agentName);
  742.         LOGGER.debug("Device class: {}", deviceClass.getCanonicalName());
  743.         String agentDisplayName = agentProperties.getProperty(AgentProperties.AGENT_DISPLAY_NAME_PROPERTY);
  744.         LOGGER.debug("Agent display name: {}", agentDisplayName);
  745.         Class<?> agentClass = classLoader.getAgentClassByAgentName(agentName);
  746.         LOGGER.debug("Agent class: {}", agentClass.getCanonicalName());
  747.  
  748.         PreparedStatement updateAgentStatement = connection.prepareStatement(queryUpdateAgentByName);
  749.         updateAgentStatement.setString(1, agentVersion);
  750.         updateAgentStatement.setString(2, agentDescription);
  751.         updateAgentStatement.setString(3, deviceClass.getCanonicalName());
  752.         updateAgentStatement.setString(4, agentDisplayName);
  753.         updateAgentStatement.setString(5, agentClass.getCanonicalName());
  754.         updateAgentStatement.setString(6, agentName);
  755.        
  756.         int updatedRows = updateAgentStatement.executeUpdate();
  757.         LOGGER.debug("Updated {} rows", updatedRows);
  758.         if (updatedRows <= 0) {
  759.             throw new Exception("Failed to update data in agents table");
  760.         }
  761.         else if (updatedRows > 1) {
  762.             LOGGER.warn("Updated more than one ({}) rows in agents table", updatedRows);
  763.         }
  764.     }
  765.    
  766.     public static void installOrUpgradeAgent(DatabaseConfiguration databaseConfig, String agentJarPath) throws Exception {
  767.         Database database = new Database(databaseConfig);
  768.         database.installOrUpgradeAgent(agentJarPath);
  769.     }
  770.    
  771.     public void installOrUpgradeAgent(String agentJarPath) throws Exception {
  772.         Connection connection = getConnection();
  773.         try {
  774.             AgentClassLoader classLoader = getAgentClassLoaderByJar(agentJarPath);
  775.             try {
  776.                 String agentName;
  777.                 try {
  778.                     agentName = classLoader.getAgentNames().iterator().next();
  779.                 }
  780.                 catch (NoSuchElementException ex) {
  781.                     String errorMsg = String.format("Failed to get agent name from JAR {}. Probably, you specified not an agent JAR.", agentJarPath);
  782.                     LOGGER.error("{}", errorMsg);
  783.                     throw new Exception(errorMsg);
  784.                 }
  785.                
  786.                 installOrUpgradeAgent(classLoader, connection, agentName);
  787.             }
  788.             finally {
  789.                 classLoader.close();
  790.             }
  791.         }
  792.         finally {
  793.             connection.close();
  794.         }
  795.     }
  796.    
  797.     private void installOrUpgradeAgent(AgentClassLoader classLoader, Connection connection, String agentName) throws Exception {
  798.         LOGGER.debug("Installing/upgrading agent {}...", agentName);
  799.        
  800.         String dialectFolder = Utils.getFolderByDialect(databaseConfig.getDialect());
  801.         LOGGER.debug("Dialect folder: {}", dialectFolder);
  802.        
  803.         Class<? extends ImportSqlCommandExtractor> commandExtractorClass = getCommandExtractorClassOrDefault();
  804.         LOGGER.debug("Command extractor class: {}", commandExtractorClass.getCanonicalName());
  805.         ImportSqlCommandExtractor commandExtractor = commandExtractorClass.newInstance();
  806.        
  807.         String encoding = getEncodingOrDefault();
  808.         LOGGER.debug("Encoding: {}", encoding);
  809.        
  810.         installOrUpgradeAgent(classLoader, connection, agentName, dialectFolder, commandExtractor, encoding);
  811.  
  812.         LOGGER.debug("Agent {} installed/upgraded", agentName);
  813.     }
  814.    
  815.     private void installOrUpgradeAgent(AgentClassLoader classLoader, Connection connection, String agentName, String dialectFolder,
  816.             ImportSqlCommandExtractor commandExtractor, String encoding) throws Exception {
  817.         if (!isAgentsTableExists(connection)) {
  818.             throw new Exception("Agents table not exists");
  819.         }
  820.  
  821.         TEM_Agent installedAgent = getInstalledAgentByName(connection, agentName);
  822.         if (installedAgent != null) {
  823.             LOGGER.debug("Agent {} is already installed, upgrading it...");
  824.             internalUpgradeAgent(classLoader, connection, agentName);
  825.         }
  826.         else {
  827.             LOGGER.debug("Agent {} is not installed, installing it...");
  828.             installAgent(classLoader, connection, agentName, dialectFolder, commandExtractor, encoding);
  829.         }
  830.     }
  831.    
  832.     public static DatabaseVersion getVersion(DatabaseConfiguration databaseConfig) throws Exception {
  833.         Database database = new Database(databaseConfig);
  834.         DatabaseVersion version = database.getVersion();
  835.         return version;
  836.     }
  837.    
  838.     /**
  839.      * Get the database version.
  840.      * Retrieves the actual and the latest versions for core and agents.
  841.      *  
  842.      * @return version
  843.      * @throws MigrationException
  844.      */
  845.     public DatabaseVersion getVersion() throws Exception {
  846.         Connection connection = getConnection();
  847.         try {
  848.             AgentClassLoader classLoader = getAgentClassLoaderByInstalledAgents(connection);
  849.             try {
  850.                 DatabaseVersion version = getVersion(classLoader);
  851.                 return version;
  852.             }
  853.             finally {
  854.                 classLoader.close();
  855.             }
  856.         }
  857.         finally {
  858.             connection.close();
  859.         }
  860.     }
  861.    
  862.     public DatabaseVersion getVersion(AgentClassLoader classLoader) throws Exception {
  863.         LOGGER.info("Getting database version...");
  864.        
  865.         Flyway flywayCore = createFlywayCore(classLoader);
  866.         Map<String, Flyway> flywayAgents = createFlywaysAgents(classLoader);
  867.        
  868.         String coreActualVersion = getMigrationsVersion(flywayCore.info().applied());
  869.         String coreLatestVersion;
  870.        
  871.         MigrationInfo[] corePendingMigrations = flywayCore.info().pending();
  872.         if (corePendingMigrations == null || corePendingMigrations.length == 0) {
  873.             coreLatestVersion = coreActualVersion;
  874.         }
  875.         else {
  876.             coreLatestVersion = getMigrationsVersion(corePendingMigrations);
  877.         }
  878.         ComponentVersions coreVersions = new ComponentVersions(coreActualVersion, coreLatestVersion);
  879.         LOGGER.debug("Core actual version: {}", coreActualVersion != null ? coreActualVersion.toString() : null);
  880.         LOGGER.debug("Core latest version: {}", coreLatestVersion != null ? coreLatestVersion.toString() : null);
  881.        
  882.         Map<String, ComponentVersions> allAgentVersions = new HashMap<>();
  883.         for (Map.Entry<String, Flyway> flywayAgentEntry : flywayAgents.entrySet()) {
  884.             String agentName = flywayAgentEntry.getKey();
  885.             Flyway flywayAgent = flywayAgentEntry.getValue();
  886.            
  887.             String agentActualVersion = getMigrationsVersion(flywayAgent.info().applied());
  888.             String agentLatestVersion;
  889.            
  890.             MigrationInfo[] agentPendingMigrations = flywayAgent.info().pending();
  891.             if (agentPendingMigrations == null || agentPendingMigrations.length == 0) {
  892.                 agentLatestVersion = agentActualVersion;
  893.             }
  894.             else {
  895.                 agentLatestVersion = getMigrationsVersion(agentPendingMigrations);
  896.             }
  897.             ComponentVersions agentVersions = new ComponentVersions(agentActualVersion, agentLatestVersion);
  898.             LOGGER.debug("Agent {} actual version: {}", agentName, agentActualVersion != null ? agentActualVersion.toString() : null);
  899.             LOGGER.debug("Agent {} latest version: {}", agentName, agentLatestVersion != null ? agentLatestVersion.toString() : null);
  900.            
  901.             allAgentVersions.put(agentName, agentVersions);
  902.         }
  903.        
  904.         return new DatabaseVersion(coreVersions, allAgentVersions);
  905.     }
  906.    
  907.     public static DatabaseMigrations getMigrations(DatabaseConfiguration databaseConfig) throws Exception {
  908.         Database database = new Database(databaseConfig);
  909.         DatabaseMigrations migrations = database.getMigrations();
  910.         return migrations;
  911.     }
  912.    
  913.     /**
  914.      * Return all migrations.
  915.      *
  916.      * @return migrations
  917.      * @throws MigrationException
  918.      */
  919.     public DatabaseMigrations getMigrations() throws Exception {
  920.         Connection connection = getConnection();
  921.         try {
  922.             AgentClassLoader classLoader = getAgentClassLoaderByInstalledAgents(connection);
  923.             try {
  924.                 DatabaseMigrations migrations = getMigrations(classLoader);
  925.                 return migrations;
  926.             }
  927.             finally {
  928.                 classLoader.close();
  929.             }
  930.         }
  931.         finally {
  932.             connection.close();
  933.         }
  934.     }
  935.    
  936.     public DatabaseMigrations getMigrations(AgentClassLoader classLoader) throws Exception {
  937.         LOGGER.info("Getting database migrations...");
  938.        
  939.         Flyway flywayCore = createFlywayCore(classLoader);
  940.         Map<String, Flyway> flywayAgents = createFlywaysAgents(classLoader);
  941.  
  942.         List<Migration> coreMigrations = getMigrations(flywayCore);
  943.         if (LOGGER.isDebugEnabled()) {
  944.             LOGGER.debug("Core migrations:");
  945.             for (Migration coreMigration : coreMigrations) {
  946.                 LOGGER.debug("{}", coreMigration.toString());
  947.             }
  948.         }
  949.        
  950.         Map<String, List<Migration>> allAgentMigrations = new HashMap<>();
  951.         for (Map.Entry<String, Flyway> flywayAgentEntry : flywayAgents.entrySet()) {
  952.             String agentName = flywayAgentEntry.getKey();
  953.             Flyway flywayAgent = flywayAgentEntry.getValue();
  954.            
  955.             List<Migration> agentMigrations = getMigrations(flywayAgent);
  956.             if (LOGGER.isDebugEnabled()) {
  957.                 LOGGER.debug("Agent {} migrations:", agentName);
  958.                 for (Migration agentMigration : agentMigrations) {
  959.                     LOGGER.debug("{}", agentMigration.toString());
  960.                 }
  961.             }
  962.            
  963.             allAgentMigrations.put(agentName, agentMigrations);
  964.         }
  965.        
  966.         return new DatabaseMigrations(coreMigrations, allAgentMigrations);
  967.     }
  968.    
  969.     public static void clean(DatabaseConfiguration databaseConfig) throws Exception {
  970.         Database database = new Database(databaseConfig);
  971.         database.clean();
  972.     }
  973.    
  974.     /**
  975.      * Cleans the database.
  976.      * !!! REMOVES ALL THE TABLES FROM THE DATABASE !!!
  977.      *
  978.      * @throws MigrationException
  979.      */
  980.     public void clean() throws Exception {
  981.         Connection connection = getConnection();
  982.         try {
  983.             AgentClassLoader classLoader = getAgentClassLoaderByInstalledAgents(connection);
  984.             try {
  985.                 clean(classLoader);
  986.             }
  987.             finally {
  988.                 classLoader.close();
  989.             }
  990.         }
  991.         finally {
  992.             connection.close();
  993.         }
  994.     }
  995.    
  996.     public void clean(AgentClassLoader classLoader) throws Exception {
  997.         LOGGER.info("Cleaning the database...");
  998.  
  999.         Flyway flywayCore = createFlywayCore(classLoader);
  1000.         Map<String, Flyway> flywayAgents = createFlywaysAgents(classLoader);
  1001.        
  1002.         try {
  1003.             for (Map.Entry<String, Flyway> flywayAgentEntry : flywayAgents.entrySet()) {
  1004.                 String agentName = flywayAgentEntry.getKey();
  1005.                 Flyway flywayAgent = flywayAgentEntry.getValue();
  1006.                
  1007.                 LOGGER.info("Cleaning database for agent {}...", agentName);
  1008.                 flywayAgent.clean();
  1009.             }
  1010.  
  1011.             LOGGER.info("Cleaning database for core...");
  1012.             flywayCore.clean();
  1013.            
  1014.             LOGGER.info("Database successfully cleaned");
  1015.         }
  1016.         catch (FlywayException ex) {
  1017.             LOGGER.error("Failed to clean database", ex);
  1018.             throw new MigrationException("Failed to clean database", ex);
  1019.         }
  1020.     }
  1021.    
  1022.     public static void baseline(DatabaseConfiguration databaseConfig, String baselineVersion, String baselineDescription) throws Exception {
  1023.         Database database = new Database(databaseConfig);
  1024.         database.baseline(baselineVersion, baselineDescription);
  1025.     }
  1026.    
  1027.     /**
  1028.      * Baseline the database.
  1029.      * This method creates initial migration based on the current database tables.
  1030.      *
  1031.      * @param baselineVersion initial database version
  1032.      * @param baselineDescription initial description
  1033.      * @throws MigrationException
  1034.      */
  1035.     public void baseline(String baselineVersion, String baselineDescription) throws Exception {
  1036.         Connection connection = getConnection();
  1037.         try {
  1038.             AgentClassLoader classLoader = getAgentClassLoaderByInstalledAgents(connection);
  1039.             try {
  1040.                 baseline(classLoader, baselineVersion, baselineDescription);
  1041.             }
  1042.             finally {
  1043.                 classLoader.close();
  1044.             }
  1045.         }
  1046.         finally {
  1047.             connection.close();
  1048.         }
  1049.     }
  1050.    
  1051.     public void baseline(AgentClassLoader classLoader, String baselineVersion, String baselineDescription) throws Exception {
  1052.         LOGGER.info("Baselining the database to version {}...", baselineVersion);
  1053.  
  1054.         Flyway flywayCore = createFlywayCore(classLoader);
  1055.         Map<String, Flyway> flywayAgents = createFlywaysAgents(classLoader);
  1056.        
  1057.         try {
  1058.             LOGGER.info("Baselining core to version {}...", baselineVersion);
  1059.             internalBaseline(flywayCore, baselineVersion, baselineDescription);
  1060.            
  1061.             for (Map.Entry<String, Flyway> flywayAgentEntry : flywayAgents.entrySet()) {
  1062.                 String agentName = flywayAgentEntry.getKey();
  1063.                 Flyway flywayAgent = flywayAgentEntry.getValue();
  1064.                
  1065.                 LOGGER.info("Baselining agent {} to version {}...", agentName, baselineVersion);
  1066.                 internalBaseline(flywayAgent, baselineVersion, baselineDescription);
  1067.             }
  1068.            
  1069.             LOGGER.info("Database successfully baselined");
  1070.         }
  1071.         catch (FlywayException ex) {
  1072.             LOGGER.error("Failed to baseline database", ex);
  1073.             throw new MigrationException("Failed to baseline database", ex);
  1074.         }
  1075.     }
  1076.    
  1077.     public static void baselineToLatestVersion(DatabaseConfiguration databaseConfig) throws Exception {
  1078.         Database database = new Database(databaseConfig);
  1079.         database.baselineToLatestVersion();
  1080.     }
  1081.    
  1082.     public void baselineToLatestVersion() throws Exception {
  1083.         Connection connection = getConnection();
  1084.         try {
  1085.             AgentClassLoader classLoader = getAgentClassLoaderByInstalledAgents(connection);
  1086.             try {
  1087.                 baselineToLatestVersion(classLoader);
  1088.             }
  1089.             finally {
  1090.                 classLoader.close();
  1091.             }
  1092.         }
  1093.         finally {
  1094.             connection.close();
  1095.         }
  1096.     }
  1097.    
  1098.     /**
  1099.      * Baseline the database to the latest available version.
  1100.      *
  1101.      * @throws MigrationException
  1102.      */
  1103.     public void baselineToLatestVersion(AgentClassLoader classLoader) throws Exception {
  1104.         LOGGER.info("Baselining the database to the latest version...");
  1105.  
  1106.         Flyway flywayCore = createFlywayCore(classLoader);
  1107.         Map<String, Flyway> flywayAgents = createFlywaysAgents(classLoader);
  1108.        
  1109.         try {
  1110.             LOGGER.info("Baselining core to latest version...");
  1111.             internalBaselineToLatestVersion(flywayCore);
  1112.            
  1113.             for (Map.Entry<String, Flyway> flywayAgentEntry : flywayAgents.entrySet()) {
  1114.                 String agentName = flywayAgentEntry.getKey();
  1115.                 Flyway flywayAgent = flywayAgentEntry.getValue();
  1116.                
  1117.                 LOGGER.info("Baselining agent {} to latest version...", agentName);
  1118.                 internalBaselineToLatestVersion(flywayAgent);
  1119.             }
  1120.            
  1121.             LOGGER.info("Database successfully baselined to the latest version");
  1122.         }
  1123.         catch (FlywayException ex) {
  1124.             LOGGER.error("Failed to baseline database", ex);
  1125.             throw new MigrationException("Failed to baseline database", ex);
  1126.         }
  1127.     }
  1128.    
  1129.     private void internalBaseline(Flyway flyway, String baselineVersion, String baselineDescription) {
  1130.         LOGGER.debug("Baselining to version {}...", baselineVersion);
  1131.         flyway.setBaselineVersionAsString(baselineVersion);
  1132.         flyway.setBaselineDescription(baselineDescription);
  1133.         flyway.baseline();
  1134.     }
  1135.  
  1136.     private void internalBaselineToLatestVersion(Flyway flyway) {
  1137.         String latestVersion = getMigrationsVersion(flyway.info().pending());
  1138.         LOGGER.debug("Baselining to latest version {}...", latestVersion);
  1139.         flyway.setBaselineVersionAsString(latestVersion);
  1140.         flyway.baseline();
  1141.     }
  1142.    
  1143.     public static boolean validate(DatabaseConfiguration databaseConfig) throws Exception {
  1144.         Database database = new Database(databaseConfig);
  1145.         boolean isValid = database.validate();
  1146.         return isValid;
  1147.     }
  1148.    
  1149.     /**
  1150.      * Validate the database.
  1151.      *
  1152.      * @return true if database is valid, false otherwise
  1153.      * @throws MigrationException
  1154.      */
  1155.     public boolean validate() throws Exception {
  1156.         Connection connection = getConnection();
  1157.         try {
  1158.             AgentClassLoader classLoader = getAgentClassLoaderByInstalledAgents(connection);
  1159.             try {
  1160.                 boolean isValid = validate(classLoader);
  1161.                 return isValid;
  1162.             }
  1163.             finally {
  1164.                 classLoader.close();
  1165.             }
  1166.         }
  1167.         finally {
  1168.             connection.close();
  1169.         }
  1170.     }
  1171.    
  1172.     public boolean validate(AgentClassLoader classLoader) throws Exception {
  1173.         LOGGER.debug("Validating the database");
  1174.  
  1175.         Flyway flywayCore = createFlywayCore(classLoader);
  1176.         Map<String, Flyway> flywayAgents = createFlywaysAgents(classLoader);
  1177.        
  1178.         try {
  1179.             LOGGER.debug("Validating core...");
  1180.             flywayCore.validate();
  1181.            
  1182.             for (Map.Entry<String, Flyway> flywayAgentEntry : flywayAgents.entrySet()) {
  1183.                 String agentName = flywayAgentEntry.getKey();
  1184.                 Flyway flywayAgent = flywayAgentEntry.getValue();
  1185.                
  1186.                 LOGGER.info("Validating agent {}...", agentName);
  1187.                 flywayAgent.validate();
  1188.             }
  1189.            
  1190.             LOGGER.debug("Database successfully validated");
  1191.             return true;
  1192.         }
  1193.         catch (FlywayException ex) {
  1194.             LOGGER.error("Validate failed", ex);
  1195.             return false;
  1196.         }
  1197.     }
  1198.    
  1199.     public static void repair(DatabaseConfiguration databaseConfig) throws Exception {
  1200.         Database database = new Database(databaseConfig);
  1201.         database.repair();
  1202.     }
  1203.    
  1204.     /**
  1205.      * Repair the database. This will perform the following actions:
  1206.      * <ul>
  1207.      * <li>Remove any failed migrations on databases without DDL transactions (User objects left behind must still be cleaned up manually)</li>
  1208.      * <li>Correct wrong checksums</li>
  1209.      * </ul>
  1210.      *
  1211.      * @throws MigrationException
  1212.      */
  1213.     public void repair() throws Exception {
  1214.         Connection connection = getConnection();
  1215.         try {
  1216.             AgentClassLoader classLoader = getAgentClassLoaderByInstalledAgents(connection);
  1217.             try {
  1218.                 repair(classLoader);
  1219.             }
  1220.             finally {
  1221.                 classLoader.close();
  1222.             }
  1223.         }
  1224.         finally {
  1225.             connection.close();
  1226.         }
  1227.     }
  1228.    
  1229.     public void repair(AgentClassLoader classLoader) throws Exception {
  1230.         LOGGER.info("Repairing the database");
  1231.  
  1232.         Flyway flywayCore = createFlywayCore(classLoader);
  1233.         Map<String, Flyway> flywayAgents = createFlywaysAgents(classLoader);
  1234.        
  1235.         try {
  1236.             LOGGER.debug("Repairing core...");
  1237.             flywayCore.repair();
  1238.            
  1239.             for (Map.Entry<String, Flyway> flywayAgentEntry : flywayAgents.entrySet()) {
  1240.                 String agentName = flywayAgentEntry.getKey();
  1241.                 Flyway flywayAgent = flywayAgentEntry.getValue();
  1242.                
  1243.                 LOGGER.info("Repairing agent {}...", agentName);
  1244.                 flywayAgent.repair();
  1245.             }
  1246.            
  1247.             LOGGER.info("Database successfully repaired");
  1248.         }
  1249.         catch (FlywayException ex) {
  1250.             LOGGER.error("Repair failed", ex);
  1251.             throw new MigrationException("Failed to repair database", ex);
  1252.         }
  1253.     }
  1254.    
  1255.     private void migrateSchema(Flyway flyway) throws MigrationException {
  1256.         LOGGER.debug("Repairing and migrating schema...");
  1257.         try {
  1258.             flyway.repair();
  1259.             flyway.migrate();
  1260.         }
  1261.         catch (FlywayException ex) {
  1262.             LOGGER.error("Failed to migrate to the latest version", ex);
  1263.             throw new MigrationException("Failed to migrate to the latest version", ex);
  1264.         }
  1265.     }
  1266.  
  1267.     /**
  1268.      * Return the locations for the database migration files.
  1269.      * This method returns the following migration locations:
  1270.      * - common SQL migration location (which
  1271.      * contains migrations for all supported databases);
  1272.      * - SQL migration location that are specific for DB used (for example,
  1273.      * postgres);
  1274.      * - Java migration package.
  1275.      *
  1276.      * @return migration locations
  1277.      * @throws MigrationException
  1278.      */
  1279.     private String[] getMigrationLocations(LocationConfiguration locationConfiguration) throws MigrationException {
  1280.         if (locationConfiguration == null) {
  1281.             return null;
  1282.         }
  1283.        
  1284.         try {
  1285.             String dialectFolder = Utils.getFolderByDialect(databaseConfig.getDialect());
  1286.             return new String[] {
  1287.                     String.format("%s.%s", locationConfiguration.getSqlMigrationsPrefix(), COMMON_SQL_MIGRATIONS_FOLDER),
  1288.                     String.format("%s.%s", locationConfiguration.getSqlMigrationsPrefix(), dialectFolder),
  1289.                     locationConfiguration.getJavaMigrationsPackage()
  1290.         };
  1291.         }
  1292.         catch (ClassNotFoundException ex) {
  1293.             throw new MigrationException(String.format("Dialect class %s not found", databaseConfig.getDialect()), ex);
  1294.         }
  1295.     }
  1296.    
  1297.     private Migration getMigration(MigrationInfo migrationInfo) throws MigrationException {
  1298.         Migration migration = new Migration();
  1299.         migration.setDescription(migrationInfo.getDescription());
  1300.         migration.setInstallationDate(migrationInfo.getInstalledOn());
  1301.         migration.setState(getMigrationState(migrationInfo.getState()));
  1302.         migration.setType(getMigrationType(migrationInfo.getType()));
  1303.         migration.setVersion(migrationInfo.getVersion().getVersion());
  1304.         return migration;
  1305.     }
  1306.    
  1307.     private MigrationState getMigrationState(org.flywaydb.core.api.MigrationState state) throws MigrationException {
  1308.         switch (state) {
  1309.             case PENDING:
  1310.                 return MigrationState.PENDING;
  1311.             case ABOVE_TARGET:
  1312.             case BELOW_BASELINE:
  1313.             case IGNORED:
  1314.                 return MigrationState.IGNORED;
  1315.             case BASELINE:
  1316.             case MISSING_SUCCESS:
  1317.             case SUCCESS:
  1318.             case OUT_OF_ORDER:
  1319.             case FUTURE_SUCCESS:
  1320.                 return MigrationState.SUCCESS;
  1321.             case MISSING_FAILED:
  1322.             case FAILED:
  1323.             case FUTURE_FAILED:
  1324.                 return MigrationState.FAILED;
  1325.             case OUTDATED:
  1326.             case SUPERSEEDED:
  1327.                 return MigrationState.OUTDATED;
  1328.             default:
  1329.                 throw new MigrationException("Unknown migration state: " + state);
  1330.         }
  1331.     }
  1332.  
  1333.     private MigrationType getMigrationType(org.flywaydb.core.api.MigrationType type) throws MigrationException {
  1334.         switch (type) {
  1335.             case BASELINE:
  1336.             case SCHEMA:
  1337.                 return MigrationType.INITIAL;
  1338.             case JDBC:
  1339.             case SPRING_JDBC:
  1340.                 return MigrationType.JAVA;
  1341.             case CUSTOM:
  1342.                 return MigrationType.CUSTOM;
  1343.             case SQL:
  1344.                 return MigrationType.SQL;
  1345.             default:
  1346.                 throw new MigrationException("Unknown migration type: " + type);
  1347.         }
  1348.     }
  1349.  
  1350.     private String getMigrationsVersion(MigrationInfo[] migrations) {
  1351.         MigrationInfo latestMigration = getLatestMigration(migrations);
  1352.         if (latestMigration == null) {
  1353.             LOGGER.debug("No migrations found.");
  1354.             return null;
  1355.         }
  1356.  
  1357.         String latestMigrationVersion = latestMigration.getVersion().getVersion();
  1358.         LOGGER.debug("The latest migration has the version {}", latestMigrationVersion);
  1359.        
  1360.         return latestMigrationVersion;
  1361.     }
  1362.    
  1363.     private MigrationInfo getLatestMigration(MigrationInfo[] migrations) {
  1364.         if (migrations == null || migrations.length == 0) {
  1365.             return null;
  1366.         }
  1367.         LOGGER.debug("{} migrations found", migrations.length);
  1368.         List<MigrationInfo> migrationsList = Arrays.asList(migrations);
  1369.         Collections.sort(migrationsList);
  1370.         return migrationsList.get(migrationsList.size() - 1);
  1371.     }
  1372.  
  1373.     private List<Migration> getMigrations(Flyway flyway) throws MigrationException {
  1374.         LOGGER.trace("getMigrations()");
  1375.         try {
  1376.             MigrationInfoService infoService = flyway.info();
  1377.             MigrationInfo[] migrationInfos = infoService.all();
  1378.             List<Migration> migrations = new ArrayList<>();
  1379.             LOGGER.debug("{} migrations", migrationInfos.length);
  1380.             for (MigrationInfo migrationInfo : migrationInfos) {
  1381.                 Migration migration = getMigration(migrationInfo);
  1382.                 LOGGER.debug("{}", migration.toString());
  1383.                 migrations.add(migration);
  1384.             }
  1385.             return migrations;
  1386.         }
  1387.         catch (FlywayException ex) {
  1388.             throw new MigrationException("Failed to get migrations", ex);
  1389.         }
  1390.     }
  1391.  
  1392.     private Flyway createFlywayCore(AgentClassLoader classLoader) throws MigrationException {
  1393.         LOGGER.debug("Creating flyway object for core...");
  1394.         Flyway flywayCore = createFlyway(classLoader, null, null);
  1395.         return flywayCore;
  1396.     }
  1397.    
  1398.     private Flyway createFlywayAgent(AgentClassLoader classLoader, String agentName) throws Exception {
  1399.         LOGGER.debug("Creating flyway object for agent {}...", agentName);
  1400.         AgentConfiguration agentConfig = getAgentConfiguration(classLoader, agentName);
  1401.         Flyway flywayAgent = createFlyway(classLoader, agentName, agentConfig);
  1402.         return flywayAgent;
  1403.     }
  1404.    
  1405.     private Map<String, Flyway> createFlywaysAgents(AgentClassLoader classLoader) throws Exception {
  1406.         Map<String, Flyway> flywayAgents = new HashMap<String, Flyway>();
  1407.         for (String agentName : classLoader.getAgentNames()) {
  1408.             Flyway flywayAgent = createFlywayAgent(classLoader, agentName);
  1409.             flywayAgents.put(agentName, flywayAgent);
  1410.         }
  1411.         return flywayAgents;
  1412.     }
  1413.  
  1414.     /**
  1415.      * Create flyway instance for core or agent
  1416.      *
  1417.      * @param agentConfiguration agent configuration if creating flyway for agent, or null
  1418.      * if creating for core
  1419.      * @return flyway
  1420.      * @throws MigrationException
  1421.      */
  1422.     private Flyway createFlyway(AgentClassLoader classLoader, String agentName, AgentConfiguration agentConfiguration) throws MigrationException {
  1423.         Flyway flyway = new Flyway();
  1424.        
  1425.         flyway.setClassLoader(classLoader);
  1426.        
  1427.         flyway.setDataSource(databaseConfig.getConnectionString(), databaseConfig.getUsername(), databaseConfig.getPassword());
  1428.        
  1429.         /*
  1430.          * Change the default placeholder prefix from the default ${ to $${
  1431.          * This is required because some our import scripts have ${...} (for
  1432.          * example, ${NMS_HOME}) that should not be expanded, and Flyway
  1433.          * complains that the placeholder cannot be resolved.
  1434.          */
  1435.         flyway.setPlaceholderPrefix(PLACEHOLDER_PREFIX);
  1436.        
  1437.         String encoding = getEncodingOrDefault();
  1438.         flyway.setEncoding(encoding);
  1439.        
  1440.         String[] locations;
  1441.         if (agentConfiguration != null) {
  1442.             locations = getMigrationLocations(agentConfiguration.getLocationConfiguration());
  1443.         }
  1444.         else {
  1445.             locations = getMigrationLocations(new LocationConfiguration(CORE_SQL_MIGRATIONS_PREFIX, CORE_JAVA_MIGRATIONS_PACKAGE));
  1446.         }
  1447.         flyway.setLocations(locations);
  1448.        
  1449.         String schema = databaseConfig.getSchema();
  1450.         if (schema != null) {
  1451.             flyway.setSchemas(schema);
  1452.         }
  1453.        
  1454.         if (agentName != null) {
  1455.             String tableName = getAgentSchemaVersionTableName(agentName);
  1456.             flyway.setTable(tableName);
  1457.         }
  1458.  
  1459.         boolean outOfOrder = getOutOfOrderOrDefault();
  1460.         flyway.setOutOfOrder(outOfOrder);
  1461.        
  1462.         boolean validateOnMigrate = getValidateOnMigrateOrDefault();
  1463.         flyway.setValidateOnMigrate(validateOnMigrate);
  1464.        
  1465.         /*
  1466.          * Without this property set to true, initial migrate will fail for agent
  1467.          * because schema is not empty.
  1468.          */
  1469.         flyway.setBaselineOnMigrate(true);
  1470.        
  1471.         return flyway;
  1472.     }
  1473.  
  1474.     public static String getAgentSchemaVersionTableName(String agentName) {
  1475.         String tableName = String.format(SCHEMA_VERSION_AGENT_TABLE_TEMPLATE, agentName);
  1476.         return tableName;
  1477.     }
  1478.  
  1479.     private Connection getConnection() throws SQLException {
  1480.         return getConnection(databaseConfig.getConnectionString(), databaseConfig.getUsername(), databaseConfig.getPassword());
  1481.     }
  1482.    
  1483.     private Connection getConnection(String connectionString, String username, String password) throws SQLException {
  1484.         Connection connection = DriverManager.getConnection(connectionString, username, password);
  1485.         return connection;
  1486.     }
  1487.    
  1488.     private AgentClassLoader getAgentClassLoaderByJar(String agentJarPath) throws MalformedURLException {
  1489.         AgentClassLoader classLoader = new AgentClassLoader();
  1490.         classLoader.addAgentFile(agentJarPath);
  1491.         return classLoader;
  1492.     }
  1493.    
  1494.     private AgentClassLoader getAgentClassLoaderByJars(String[] agentJarPaths) throws MalformedURLException {
  1495.         AgentClassLoader classLoader = new AgentClassLoader();
  1496.         classLoader.addAgentFiles(agentJarPaths);
  1497.         return classLoader;
  1498.     }
  1499.  
  1500.     private AgentClassLoader getAgentClassLoaderByDirectory(String agentDirectory) {
  1501.         AgentClassLoader classLoader = new AgentClassLoader();
  1502.         AgentDirectoryPlaceholderResolver placeholderResolver = new AgentDirectoryPlaceholderResolver();
  1503.         String resolvedAgentDirectory = placeholderResolver.replacePlaceholders(agentDirectory);
  1504.         LOGGER.debug("Resovled agent directory: {} -> {}", agentDirectory, resolvedAgentDirectory);
  1505.         classLoader.addAgentFolder(resolvedAgentDirectory);
  1506.         return classLoader;
  1507.     }
  1508.    
  1509.     private AgentClassLoader getAgentClassLoaderByInstalledAgents(Connection connection) throws SQLException {
  1510.         AgentClassLoader classLoader = new AgentClassLoader();
  1511.         AgentDirectoryPlaceholderResolver placeholderResolver = new AgentDirectoryPlaceholderResolver();
  1512.         List<TEM_Agent> installedAgents = getInstalledAgents(connection);
  1513.         for (TEM_Agent installedAgent : installedAgents) {
  1514.             String agentDirectory = installedAgent.getDirectory();
  1515.             String resolvedAgentDirectory = placeholderResolver.replacePlaceholders(agentDirectory);
  1516.             LOGGER.debug("Resolved agent directory: {} -> {}", agentDirectory, resolvedAgentDirectory);
  1517.             classLoader.addAgentFolder(resolvedAgentDirectory);
  1518.         }
  1519.         return classLoader;
  1520.     }
  1521.    
  1522.     private static final String queryGetInstalledAgents = "SELECT id, agentversion, description, deviceclass, directory, displayname, installationdate, name, agentclass FROM tem_agent";
  1523.    
  1524.     private List<TEM_Agent> getInstalledAgents(Connection connection) throws SQLException {
  1525.         if (!isAgentsTableExists(connection)) {
  1526.             return new ArrayList<TEM_Agent>();
  1527.         }
  1528.        
  1529.         Statement statement = connection.createStatement();
  1530.         ResultSet agentsResultSet = statement.executeQuery(queryGetInstalledAgents);
  1531.         List<TEM_Agent> result = new ArrayList<TEM_Agent>();
  1532.         while (agentsResultSet.next()) {
  1533.             TEM_Agent agent = getAgentFromResultSet(agentsResultSet);
  1534.             result.add(agent);
  1535.         }
  1536.         return result;
  1537.     }
  1538.    
  1539.     private static final String queryGetInstalledAgentById = "SELECT id, agentversion, description, deviceclass, directory, displayname, installationdate, name, agentclass FROM tem_agent WHERE id=?";
  1540.    
  1541.     private TEM_Agent getInstalledAgentById(Connection connection, Long agentId) throws SQLException {
  1542.         PreparedStatement statement = connection.prepareStatement(queryGetInstalledAgentById);
  1543.         statement.setLong(1, agentId);
  1544.         ResultSet agentResultSet = statement.executeQuery();
  1545.         if (agentResultSet.next()) {
  1546.             TEM_Agent agent = getAgentFromResultSet(agentResultSet);
  1547.             return agent;
  1548.         }
  1549.         else {
  1550.             return null;
  1551.         }
  1552.     }
  1553.    
  1554.     private static final String queryGetInstalledAgentByName = "SELECT id, agentversion, description, deviceclass, directory, displayname, installationdate, name, agentclass FROM tem_agent WHERE name=?";
  1555.    
  1556.     private TEM_Agent getInstalledAgentByName(Connection connection, String agentName) throws SQLException {
  1557.         PreparedStatement statement = connection.prepareStatement(queryGetInstalledAgentByName);
  1558.         statement.setString(1, agentName);
  1559.         ResultSet agentResultSet = statement.executeQuery();
  1560.         if (agentResultSet.next()) {
  1561.             TEM_Agent agent = getAgentFromResultSet(agentResultSet);
  1562.             return agent;
  1563.         }
  1564.         else {
  1565.             return null;
  1566.         }
  1567.     }
  1568.  
  1569.     private TEM_Agent getAgentFromResultSet(ResultSet resultSet) throws SQLException {
  1570.         Long id = resultSet.getLong("id");
  1571.         String agentVersion = resultSet.getString("agentversion");
  1572.         String description = resultSet.getString("description");
  1573.         String deviceClass = resultSet.getString("deviceclass");
  1574.         String directory = resultSet.getString("directory");
  1575.         String displayName = resultSet.getString("displayname");
  1576.         Date installationDate = resultSet.getDate("installationdate");
  1577.         String name = resultSet.getString("name");
  1578.         String agentClass = resultSet.getString("agentclass");
  1579.        
  1580.         TEM_Agent agent = new TEM_Agent();
  1581.         agent.setId(id);
  1582.         agent.setAgentVersion(agentVersion);
  1583.         agent.setDescription(description);
  1584.         agent.setDeviceClass(deviceClass);
  1585.         agent.setDirectory(directory);
  1586.         agent.setDisplayName(displayName);
  1587.         agent.setInstallationDate(installationDate);
  1588.         agent.setName(name);
  1589.         agent.setAgentClass(agentClass);
  1590.        
  1591.         return agent;
  1592.     }
  1593.    
  1594.     private static final String queryInsertAgent_postgres = "INSERT INTO tem_agent(id, version, agentversion, description, deviceclass, directory, displayname, installationdate, name, agentclass) " +
  1595.             "VALUES(nextval('id_sequence'), 0, ?, ?, ?, ?, ?, ?, ?, ?)";
  1596.    
  1597.     private static final String queryInsertAgent_hsqldb = "INSERT INTO tem_agent(id, version, agentversion, description, deviceclass, directory, displayname, installationdate, name, agentclass) " +
  1598.             "VALUES(nextval('id_sequence'), 0, ?, ?, ?, ?, ?, ?, ?, ?)";
  1599.    
  1600.     private String getInsertAgentQuery(String dialectFolder) throws Exception {
  1601.         if (Utils.FOLDER_POSTGRES.equals(dialectFolder)) {
  1602.             return queryInsertAgent_postgres;
  1603.         }
  1604.         else if (Utils.FOLDER_HSQLDB.equals(dialectFolder)) {
  1605.             return queryInsertAgent_hsqldb;
  1606.         }
  1607.         else if (Utils.FOLDER_ORACLE.equals(dialectFolder)) {
  1608.             throw new Exception(String.format("Inserting into agents table is not implemented for dialect %s", dialectFolder));
  1609.         }
  1610.         else {
  1611.             throw new Exception(String.format("Dialect %s not supported", dialectFolder));
  1612.         }
  1613.     }
  1614.    
  1615.     private Properties getAgentProperties(AgentClassLoader classLoader, String agentName) throws Exception {
  1616.         Properties properties = classLoader.getPropertiesByAgentName(agentName);
  1617.         if (properties == null) {
  1618.             throw new Exception(String.format("Properties for agent %s not found", agentName));
  1619.         }
  1620.         return properties;
  1621.     }
  1622.  
  1623.     private AgentConfiguration getAgentConfiguration(AgentClassLoader classLoader, String agentName) throws Exception {
  1624.         Properties agentProperties = getAgentProperties(classLoader, agentName);
  1625.        
  1626.         String sqlMigrationsPrefix = agentProperties.getProperty(AgentProperties.AGENT_SQL_MIGRATIONS_PREFIX_PROPERTY);
  1627.         LOGGER.debug("Agent {} SQL migrations prefix: {}", agentName, sqlMigrationsPrefix);
  1628.         String javaMigrationsPackage = agentProperties.getProperty(AgentProperties.AGENT_JAVA_MIGRATIONS_PACKAGE_PROPERTY);
  1629.         LOGGER.debug("Agent {} Java migrations package: {}", agentName, javaMigrationsPackage);
  1630.            
  1631.         AgentConfiguration agentConfig = new AgentConfiguration(new LocationConfiguration(sqlMigrationsPrefix, javaMigrationsPackage));
  1632.         return agentConfig;
  1633.     }
  1634.    
  1635.     private String getAgentSqlResourceFolder(AgentClassLoader classLoader, String agentName) throws Exception {
  1636.         Properties agentProperties = getAgentProperties(classLoader, agentName);
  1637.         String resourceFolder = agentProperties.getProperty(AgentProperties.AGENT_SQL_FOLDER_PROPERTY);
  1638.         LOGGER.debug("Agent {} SQL folder: {}", agentName, resourceFolder);
  1639.         return resourceFolder;
  1640.     }
  1641.    
  1642.     private String[] loadCommands(ClassLoader classLoader, ImportSqlCommandExtractor commandExtractor,
  1643.             String resourceFolder, String dialectFolder, String[] scriptFileNames, String encoding) throws IOException {
  1644.         List<String> allScriptCommands = new ArrayList<String>();
  1645.         for (String scriptFileName : scriptFileNames) {
  1646.             String[] scriptCommands = loadCommands(classLoader, commandExtractor, resourceFolder, dialectFolder, scriptFileName, encoding);
  1647.             if (scriptCommands != null) {
  1648.                 allScriptCommands.addAll(Arrays.asList(scriptCommands));
  1649.             }
  1650.         }
  1651.         return allScriptCommands.toArray(new String[allScriptCommands.size()]);
  1652.     }
  1653.    
  1654.     private String[] loadCommands(ClassLoader classLoader, ImportSqlCommandExtractor commandExtractor,
  1655.             String resourceFolder, String dialectFolder, String scriptFileName, String encoding) throws IOException {
  1656.         LOGGER.debug("Loading commands from file {}", scriptFileName);
  1657.  
  1658.         String scriptFilePath = getScriptFilePath(resourceFolder, dialectFolder, scriptFileName);
  1659.         LOGGER.debug("Loading commands from resource {}...", scriptFilePath);
  1660.        
  1661.         try (InputStream is = classLoader.getResourceAsStream(scriptFilePath)) {
  1662.             if (is == null) {
  1663.                 LOGGER.info("Resource {} not found", scriptFilePath);
  1664.                 return null;
  1665.             }
  1666.            
  1667.             LOGGER.info("Resource {} found", scriptFilePath);
  1668.            
  1669.             InputStreamReader reader = new InputStreamReader(is, encoding);
  1670.             LOGGER.debug("Extracting commands from resource {}", scriptFilePath);
  1671.             String[] commands = commandExtractor.extractCommands(reader);
  1672.             LOGGER.debug("Extracted {} commands from resource {}", commands.length, scriptFilePath);
  1673.             return commands;
  1674.         }
  1675.     }
  1676.    
  1677.     private String loadScriptAsString(ClassLoader classLoader, String resourceFolder, String dialectFolder,
  1678.             String scriptFileName, String encoding) throws IOException {
  1679.         LOGGER.debug("Loading script from file {}", scriptFileName);
  1680.  
  1681.         String scriptFilePath = getScriptFilePath(resourceFolder, dialectFolder, scriptFileName);
  1682.         LOGGER.debug("Loading script from resource {}...", scriptFilePath);
  1683.  
  1684.         try (InputStream is = classLoader.getResourceAsStream(scriptFilePath)) {
  1685.             if (is == null) {
  1686.                 LOGGER.info("Resource {} not found", scriptFilePath);
  1687.                 return null;
  1688.             }
  1689.            
  1690.             LOGGER.info("Resource {} found", scriptFilePath);
  1691.            
  1692.             String script = IOUtils.toString(is, encoding);
  1693.             return script;
  1694.         }
  1695.     }
  1696.  
  1697.     private String getScriptFilePath(String resourceFolder, String dialectFolder, String scriptFileName) {
  1698.         return String.format("%s/%s/%s", resourceFolder, dialectFolder, scriptFileName);
  1699.     }
  1700.    
  1701.     private List<Exception> executeCommands(Connection connection, String[] commands) throws SQLException {
  1702.         List<Exception> exceptions = new ArrayList<Exception>();
  1703.         if (commands != null) {
  1704.             Statement statement = connection.createStatement();
  1705.             for (String command : commands) {
  1706.                 try {
  1707.                     LOGGER.debug("Executing command {}", command);
  1708.                     statement.execute(command);
  1709.                 }
  1710.                 catch (SQLException ex) {
  1711.                     exceptions.add(ex);
  1712.                 }
  1713.             }
  1714.         }
  1715.         return exceptions;
  1716.     }
  1717.    
  1718.     private void executeScript(Connection connection, String script) throws SQLException {
  1719.         Statement statement = connection.createStatement();
  1720.         statement.execute(script);
  1721.     }
  1722.  
  1723.     private Class<? extends ImportSqlCommandExtractor> getCommandExtractorClassOrDefault() {
  1724.         Class<? extends ImportSqlCommandExtractor> commandExtractorClass = databaseConfig.getCommandExtractorClass();
  1725.         if (commandExtractorClass == null) {
  1726.             commandExtractorClass = DatabaseConfiguration.DEFAULT_COMMAND_EXTRACTOR_CLASS;
  1727.         }
  1728.         return commandExtractorClass;
  1729.     }
  1730.  
  1731.     private String getEncodingOrDefault() {
  1732.         String encoding = databaseConfig.getEncoding();
  1733.         if (encoding == null) {
  1734.             encoding = DatabaseConfiguration.DEFAULT_ENCODING;
  1735.         }
  1736.         return encoding;
  1737.     }
  1738.    
  1739.     private boolean getOutOfOrderOrDefault() {
  1740.         Boolean outOfOrder = databaseConfig.isOutOfOrder();
  1741.         if (outOfOrder != null) {
  1742.             return outOfOrder.booleanValue();
  1743.         }
  1744.         else {
  1745.             return DatabaseConfiguration.DEFAULT_OUT_OF_ORDER;
  1746.         }
  1747.     }
  1748.    
  1749.     private boolean getValidateOnMigrateOrDefault() {
  1750.         Boolean validateOnMigrate = databaseConfig.isValidateOnMigrate();
  1751.         if (validateOnMigrate != null) {
  1752.             return validateOnMigrate.booleanValue();
  1753.         }
  1754.         else {
  1755.             return DatabaseConfiguration.DEFAULT_VALIDATE_ON_MIGRATE;
  1756.         }
  1757.     }
  1758.    
  1759.     /**
  1760.      * Checks if table exists.
  1761.      * We just try to select from agents table, and if unsuccessful, return false.
  1762.      * First version of this method tried to get connection metadata, but we must
  1763.      * know the schema name for that, and getting current schema name is not easy
  1764.      * using JDBC.
  1765.      *
  1766.      * @param connection
  1767.      * @return
  1768.      */
  1769.     private boolean isAgentsTableExists(Connection connection) {
  1770.         try {
  1771.             Statement statement = connection.createStatement();
  1772.             statement.executeQuery(queryGetInstalledAgents);
  1773.             LOGGER.debug("Agents table exists");
  1774.             return true;
  1775.         }
  1776.         catch (SQLException ex) {
  1777.             LOGGER.debug("Failed to get installed agents", ex);
  1778.             return false;
  1779.         }
  1780.     }
  1781.    
  1782. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement