Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions conf/app_config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Bootstrap configuration for ZStack
This file determines which properties file to load at startup

The propertiesFile value can be replaced by build script for OEM customization:
- Default: zstack.properties
- Custom: will be replaced by Ant during build (e.g., myapp.properties)
-->
<bootstrap>
<!-- BUILD_REPLACE_MARKER: Do not modify manually, will be replaced during build -->
<propertiesFile>zstack.properties</propertiesFile>
</bootstrap>
61 changes: 41 additions & 20 deletions conf/install/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -20,14 +20,34 @@ 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:.*<propertiesFile>\(.*\)</propertiesFile>.*:\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]

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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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:
5 changes: 4 additions & 1 deletion conf/zstack.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="locations">
<list>
<value>classpath:zstack.properties</value>
<!-- Properties file name is dynamically determined by app_config.xml -->
<!-- Platform.java sets the system property 'app.properties.file' at startup -->
<value>classpath:${app.properties.file}</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
</bean>

Expand Down
3 changes: 2 additions & 1 deletion conf/zstackSimulator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="locations">
<list>
<value>classpath:zstack.properties</value>
<value>classpath:${app.properties.file}</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>

Expand Down
14 changes: 13 additions & 1 deletion core/src/main/java/org/zstack/core/Platform.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Comment on lines +56 to +59
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 检查 Platform.java 中是否使用了 XML 解析类
rg -n "DocumentBuilder|DocumentBuilderFactory|Document|NodeList" core/src/main/java/org/zstack/core/Platform.java | grep -v "^.*import"

Repository: MatheMatrix/zstack

Length of output: 44


🏁 Script executed:

# 查看Platform.java的完整导入部分
head -80 core/src/main/java/org/zstack/core/Platform.java | tail -40

Repository: MatheMatrix/zstack

Length of output: 1449


🏁 Script executed:

# 搜索Platform.java中是否确实有AppConfig相关的代码
rg -n "AppConfig" core/src/main/java/org/zstack/core/Platform.java

Repository: MatheMatrix/zstack

Length of output: 184


🏁 Script executed:

# 检查AppConfig.java是否使用了这些XML类
rg -n "DocumentBuilder|DocumentBuilderFactory|Document|NodeList" core/src/main/java/org/zstack/core/config/AppConfig.java | head -20

Repository: MatheMatrix/zstack

Length of output: 511


移除 Platform.java 中未使用的 XML 导入。

DocumentBuilderDocumentBuilderFactoryDocumentNodeList 这些 XML 解析类在 Platform.java 中并未使用。XML 解析逻辑完全封装在 AppConfig 类内部。Platform.java 仅通过调用 AppConfig.getPropertiesFileName() 与之交互,无需这些导入。请移除第 56-59 行的这四个 XML 相关导入。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/src/main/java/org/zstack/core/Platform.java` around lines 56 - 59,
Platform.java contains unused XML imports (DocumentBuilder,
DocumentBuilderFactory, Document, NodeList); remove those four import lines so
the class only imports what it actually uses (it should interact with AppConfig
via AppConfig.getPropertiesFileName()). Locate the import block in Platform.java
and delete the lines referencing DocumentBuilder, DocumentBuilderFactory,
Document, and NodeList to eliminate the unused dependencies.

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
Expand Down Expand Up @@ -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);
Expand Down
68 changes: 68 additions & 0 deletions core/src/main/java/org/zstack/core/config/AppConfig.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
3 changes: 2 additions & 1 deletion test/src/test/java/org/zstack/test/DBUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion test/src/test/resources/zstack-template.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="locations">
<list>
<value>classpath:zstack.properties</value>
<value>classpath:${app.properties.file}</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreUnresolvablePlaceholders" value="true" />
</bean>

Expand Down
37 changes: 19 additions & 18 deletions testlib/src/main/java/org/zstack/testlib/Test.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<String, String> hostAndPort = getHostAndPort(System.getProperty("DB.url"))
Expand Down