diff --git a/conf/app_config.xml b/conf/app_config.xml new file mode 100644 index 00000000000..9519ca66e05 --- /dev/null +++ b/conf/app_config.xml @@ -0,0 +1,13 @@ + + + + + zstack.properties + \ No newline at end of file diff --git a/conf/install/install.sh b/conf/install/install.sh index 74b680a416b..5413f355211 100755 --- a/conf/install/install.sh +++ b/conf/install/install.sh @@ -5,7 +5,7 @@ zstack_service="/etc/init.d/zstack-server" tomcat_folder_path="" zstack_property_folder=~/.zstack/ [ ! -d $zstack_property_folder ] && mkdir $zstack_property_folder || exit 1 -old_zstack_property=$zstack_property_folder/zstack.properties-old +old_zstack_property="" tomcat_path_save=$zstack_property_folder/tomcat_path [ -f $tomcat_path_save ] && tomcat_folder_path=`cat $tomcat_path_save` @@ -20,6 +20,26 @@ set -u # undefined variables are errors error_tmp_file=`mktemp` trap "rm $error_tmp_file 2>/dev/null" EXIT +get_properties_file_name(){ + app_config_file="$1/WEB-INF/classes/zstack/conf/app_config.xml" + properties_file="zstack.properties" + + if [ -f "$app_config_file" ]; then + configured_file=`sed -n 's:.*\(.*\).*:\1:p' "$app_config_file" | head -n 1` + if [ -n "$configured_file" ]; then + properties_file=$configured_file + fi + fi + + echo "$properties_file" +} + +set_zstack_property_path(){ + properties_file_name=`get_properties_file_name "$zstack_folder"` + zstack_property="$zstack_folder/WEB-INF/classes/$properties_file_name" + old_zstack_property="$zstack_property_folder/${properties_file_name}-old" +} + help (){ echo "Usage: $0 [options] [TOMCAT_PATH] @@ -27,7 +47,7 @@ Description: ZStack Installer will install zstack.war to the given Tomcat folder. It is assumed all tools and services are installed, including Tomcat, MySQL server, RabbitMQ server, Ansible etc. The default behavior (without -f) -will upgrade current ZStack and keep previous zstack.properties. +will upgrade current ZStack and keep previous management properties file. Without command line options, it will pop up a menu for user interaction. Options: @@ -195,7 +215,7 @@ config_tomcat(){ webapp_folder=$tomcat_folder_path/webapps zstack_folder=$webapp_folder/zstack - zstack_property="$zstack_folder/WEB-INF/classes/zstack.properties" + set_zstack_property_path set_db_config tput clear tput sgr0 @@ -232,21 +252,21 @@ check_tomcat(){ } set_db_config(){ - #base on old zstack.properties to get current db configurations. - if [ -f $zstack_property ];then - /bin/cp -f $zstack_property $old_zstack_property - db_info=`grep 'DbFacadeDataSource.jdbcUrl' $zstack_property|awk -F'//' '{print $2}'|awk -F'/' '{print $1}'` + #base on old management properties file to get current db configurations. + if [ -f "$zstack_property" ];then + /bin/cp -f "$zstack_property" "$old_zstack_property" + db_info=`grep 'DbFacadeDataSource.jdbcUrl' "$zstack_property"|awk -F'//' '{print $2}'|awk -F'/' '{print $1}'` db_df_host=`echo $db_info|awk -F: '{print $1}'` db_df_port=`echo $db_info|awk -F: '{print $2}'` - db_df_user=`grep 'DbFacadeDataSource.user' $zstack_property|awk -F= '{print $2}'|tr -d '\r'|tr -d '\n'` - db_df_passwd=`grep 'DbFacadeDataSource.password' $zstack_property|awk -F= '{print $2}'|tr -d '\r'|tr -d '\n'` + db_df_user=`grep 'DbFacadeDataSource.user' "$zstack_property"|awk -F= '{print $2}'|tr -d '\r'|tr -d '\n'` + db_df_passwd=`grep 'DbFacadeDataSource.password' "$zstack_property"|awk -F= '{print $2}'|tr -d '\r'|tr -d '\n'` else - if [ -f $old_zstack_property ]; then - db_info=`grep 'DbFacadeDataSource.jdbcUrl' $old_zstack_property|awk -F'//' '{print $2}'|awk -F'/' '{print $1}'` + if [ -f "$old_zstack_property" ]; then + db_info=`grep 'DbFacadeDataSource.jdbcUrl' "$old_zstack_property"|awk -F'//' '{print $2}'|awk -F'/' '{print $1}'` db_df_host=`echo $db_info|awk -F: '{print $1}'` db_df_port=`echo $db_info|awk -F: '{print $2}'` - db_df_user=`grep 'DbFacadeDataSource.user' $old_zstack_property|awk -F= '{print $2}'|tr -d '\r'|tr -d '\n'` - db_df_passwd=`grep 'DbFacadeDataSource.password' $old_zstack_property|awk -F= '{print $2}'|tr -d '\r'|tr -d '\n'` + db_df_user=`grep 'DbFacadeDataSource.user' "$old_zstack_property"|awk -F= '{print $2}'|tr -d '\r'|tr -d '\n'` + db_df_passwd=`grep 'DbFacadeDataSource.password' "$old_zstack_property"|awk -F= '{print $2}'|tr -d '\r'|tr -d '\n'` else db_df_host=localhost db_df_port=3306 @@ -296,7 +316,7 @@ if [ $interactive_install -eq 0 ]; then tomcat_folder_path=$1 webapp_folder=$tomcat_folder_path/webapps zstack_folder=$webapp_folder/zstack - zstack_property="$zstack_folder/WEB-INF/classes/zstack.properties" + set_zstack_property_path set_db_config else config_tomcat @@ -377,11 +397,12 @@ cp $zstack_war $webapp_folder cd $webapp_folder unzip -q -d zstack zstack.war +set_zstack_property_path if [ $fresh_install -eq 1 ]; then - sed -i "s/DbFacadeDataSource.user.*/DbFacadeDataSource.user=$db_user_name/" $zstack_property - sed -i "s/DbFacadeDataSource.password.*/DbFacadeDataSource.password=$db_user_passwd/" $zstack_property - sed -i "s/DbFacadeDataSource.jdbcUrl.*/DbFacadeDataSource.jdbcUrl=jdbc:mysql:\/\/$db_host:$db_port\/zstack/" $zstack_property - sed -i "s/RestApiDataSource.jdbcUrl.*/RestApiDataSource.jdbcUrl=jdbc:mysql:\/\/$db_host:$db_port\/zstack_rest/" $zstack_property + sed -i "s/DbFacadeDataSource.user.*/DbFacadeDataSource.user=$db_user_name/" "$zstack_property" + sed -i "s/DbFacadeDataSource.password.*/DbFacadeDataSource.password=$db_user_passwd/" "$zstack_property" + sed -i "s/DbFacadeDataSource.jdbcUrl.*/DbFacadeDataSource.jdbcUrl=jdbc:mysql:\/\/$db_host:$db_port\/zstack/" "$zstack_property" + sed -i "s/RestApiDataSource.jdbcUrl.*/RestApiDataSource.jdbcUrl=jdbc:mysql:\/\/$db_host:$db_port\/zstack_rest/" "$zstack_property" db_script_folder=zstack/WEB-INF/classes/db database=$db_script_folder/database.sql @@ -417,7 +438,7 @@ if [ $fresh_install -eq 1 ]; then create_zstack_db $schema_rest update_zstack_db "zstack_quartz" $schema_quartz else - /bin/cp -f $old_zstack_property $zstack_property + /bin/cp -f "$old_zstack_property" "$zstack_property" fi zstack_service_script=$webapp_folder/zstack/WEB-INF/classes/install/zstack-server @@ -437,6 +458,6 @@ else echo -e "$(tput setaf 2)ZStack has been upgraded in $webapp_folder\n$(tput sgr0)" fi echo -e "$(tput setaf 2)zstack-server has been installed to /etc/init.d . Run \`/etc/init.d/zstack-server start\` to start zstack service.\n$(tput sgr0)" -[ -f $old_zstack_property ] && echo -e "$(tput setaf 2)Original zstack.properties was saved in $old_zstack_property\n$(tput sgr0)" +[ -f "$old_zstack_property" ] && echo -e "$(tput setaf 2)Original `basename $zstack_property` was saved in $old_zstack_property\n$(tput sgr0)" # vim: set et ts=4 sw=4 ai: diff --git a/conf/zstack.xml b/conf/zstack.xml index ed752bf46ba..d054d019034 100755 --- a/conf/zstack.xml +++ b/conf/zstack.xml @@ -22,9 +22,12 @@ - classpath:zstack.properties + + + classpath:${app.properties.file} + diff --git a/conf/zstackSimulator.xml b/conf/zstackSimulator.xml index 8fc6028b201..f24deb79bd3 100755 --- a/conf/zstackSimulator.xml +++ b/conf/zstackSimulator.xml @@ -21,9 +21,10 @@ - classpath:zstack.properties + classpath:${app.properties.file} + diff --git a/core/src/main/java/org/zstack/core/Platform.java b/core/src/main/java/org/zstack/core/Platform.java index a8d8ae191ed..9703394df34 100755 --- a/core/src/main/java/org/zstack/core/Platform.java +++ b/core/src/main/java/org/zstack/core/Platform.java @@ -21,6 +21,7 @@ import org.zstack.core.statemachine.StateMachine; import org.zstack.core.statemachine.StateMachineImpl; import org.zstack.core.thread.ThreadFacade; +import org.zstack.core.config.AppConfig; import org.zstack.header.Component; import org.zstack.header.core.StaticInit; import org.zstack.header.core.encrypt.ENCRYPT; @@ -52,6 +53,10 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.Inet4Address; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; @@ -488,7 +493,14 @@ private static void prepareHibernateSearchProperties() { } } - File globalPropertiesFile = PathUtil.findFileOnClassPath("zstack.properties", true); + // Load properties file name from app configuration + // This allows OEM customization by replacing app_config.xml during build + String propertiesFileName = AppConfig.getPropertiesFileName(); + + // Set system property for Spring to use the same file + System.setProperty("app.properties.file", propertiesFileName); + + File globalPropertiesFile = PathUtil.findFileOnClassPath(propertiesFileName, true); in = new FileInputStream(globalPropertiesFile); System.getProperties().load(in); diff --git a/core/src/main/java/org/zstack/core/config/AppConfig.java b/core/src/main/java/org/zstack/core/config/AppConfig.java new file mode 100644 index 00000000000..a2c5a0e016b --- /dev/null +++ b/core/src/main/java/org/zstack/core/config/AppConfig.java @@ -0,0 +1,68 @@ +package org.zstack.core.config; + +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.zstack.utils.path.PathUtil; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; + +/** + * Centralized configuration reader for app_config.xml + * This class provides a single source of truth for the properties file name + * All other components should use this class instead of hardcoding "zstack.properties" + */ +public class AppConfig { + private static final String DEFAULT_PROPERTIES_FILE = "zstack.properties"; + private static volatile String propertiesFileName = null; + + /** + * Get the properties file name from app_config.xml + * This method is thread-safe and caches the result + * + * @return properties file name (e.g., "zstack.properties", "myapp.properties") + */ + public static String getPropertiesFileName() { + if (propertiesFileName == null) { + synchronized (AppConfig.class) { + if (propertiesFileName == null) { + propertiesFileName = loadPropertiesFileNameFromConfig(); + } + } + } + return propertiesFileName; + } + + /** + * Load properties file name from app_config.xml + * Falls back to "zstack.properties" if app_config.xml is not found or cannot be parsed + */ + private static String loadPropertiesFileNameFromConfig() { + try { + File appConfigFile = PathUtil.findFileOnClassPath("app_config.xml", true); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + Document doc = builder.parse(appConfigFile); + + NodeList nodes = doc.getElementsByTagName("propertiesFile"); + if (nodes.getLength() > 0) { + String fileName = nodes.item(0).getTextContent().trim(); + System.out.println("[AppConfig] Using properties file: " + fileName); + return fileName; + } + } catch (Exception e) { + System.err.println("[AppConfig] Failed to load app_config.xml, using default: " + DEFAULT_PROPERTIES_FILE); + e.printStackTrace(); + } + + return DEFAULT_PROPERTIES_FILE; + } + + /** + * Reset cached value (mainly for testing) + */ + public static void reset() { + propertiesFileName = null; + } +} \ No newline at end of file diff --git a/test/src/test/java/org/zstack/test/DBUtil.java b/test/src/test/java/org/zstack/test/DBUtil.java index 1f1e7c18583..72ad41a7a5a 100755 --- a/test/src/test/java/org/zstack/test/DBUtil.java +++ b/test/src/test/java/org/zstack/test/DBUtil.java @@ -2,6 +2,7 @@ import org.apache.commons.io.FileUtils; import org.zstack.core.Platform; +import org.zstack.core.config.AppConfig; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.utils.ShellResult; import org.zstack.utils.ShellUtils; @@ -28,7 +29,7 @@ public static void reDeployDB() { Properties prop = new Properties(); try { - prop.load(DBUtil.class.getClassLoader().getResourceAsStream("zstack.properties")); + prop.load(DBUtil.class.getClassLoader().getResourceAsStream(AppConfig.getPropertiesFileName())); String user = System.getProperty("DB.user"); if (user == null) { diff --git a/test/src/test/resources/zstack-template.xml b/test/src/test/resources/zstack-template.xml index 5a1c525669c..a32b58d5d83 100755 --- a/test/src/test/resources/zstack-template.xml +++ b/test/src/test/resources/zstack-template.xml @@ -17,9 +17,10 @@ - classpath:zstack.properties + classpath:${app.properties.file} + diff --git a/testlib/src/main/java/org/zstack/testlib/Test.groovy b/testlib/src/main/java/org/zstack/testlib/Test.groovy index 553d3084959..382266d24b3 100755 --- a/testlib/src/main/java/org/zstack/testlib/Test.groovy +++ b/testlib/src/main/java/org/zstack/testlib/Test.groovy @@ -3,13 +3,14 @@ package org.zstack.testlib import com.google.gson.JsonParser import com.google.gson.JsonSyntaxException import okhttp3.OkHttpClient -import org.apache.commons.lang.StringUtils -import org.zstack.core.Platform -import org.zstack.core.StartMode -import org.zstack.core.cloudbus.CloudBus -import org.zstack.core.cloudbus.CloudBusImpl2 -import org.zstack.core.componentloader.ComponentLoader -import org.zstack.core.db.DatabaseFacade +import org.apache.commons.lang.StringUtils +import org.zstack.core.Platform +import org.zstack.core.StartMode +import org.zstack.core.cloudbus.CloudBus +import org.zstack.core.cloudbus.CloudBusImpl2 +import org.zstack.core.componentloader.ComponentLoader +import org.zstack.core.config.AppConfig +import org.zstack.core.db.DatabaseFacade import org.zstack.header.AbstractService import org.zstack.header.exception.CloudRuntimeException import org.zstack.header.identity.AccountConstant @@ -261,28 +262,28 @@ abstract class Test extends ApiHelper implements Retry { Properties prop = new Properties() try { - prop.load(this.getClass().getClassLoader().getResourceAsStream("zstack.properties")) + prop.load(this.getClass().getClassLoader().getResourceAsStream(AppConfig.getPropertiesFileName())) String user = System.getProperty("DB.user") if (user == null) { user = prop.getProperty("DB.user") if (user == null) { - user = prop.getProperty("DbFacadeDataSource.user") - } - if (user == null) { - throw new CloudRuntimeException("cannot find DB user in zstack.properties, please set either DB.user or DbFacadeDataSource.user") - } + user = prop.getProperty("DbFacadeDataSource.user") + } + if (user == null) { + throw new CloudRuntimeException(String.format("cannot find DB user in %s, please set either DB.user or DbFacadeDataSource.user", AppConfig.getPropertiesFileName())) + } } String password = System.getProperty("DB.password") if (password == null) { password = prop.getProperty("DB.password") if (password == null) { - password = prop.getProperty("DbFacadeDataSource.password") - } - if (password == null) { - throw new CloudRuntimeException("cannot find DB user in zstack.properties, please set either DB.password or DbFacadeDataSource.password") - } + password = prop.getProperty("DbFacadeDataSource.password") + } + if (password == null) { + throw new CloudRuntimeException(String.format("cannot find DB password in %s, please set either DB.password or DbFacadeDataSource.password", AppConfig.getPropertiesFileName())) + } } Map hostAndPort = getHostAndPort(System.getProperty("DB.url"))