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"))