Help Center/ ServiceStage/ FAQs/ Application Development/ How Do I Package a Java or Tomcat Application?
Updated on 2025-07-07 GMT+08:00

How Do I Package a Java or Tomcat Application?

ServiceStage allows you to compress a Java or Tomcat application into a .zip or .tar.gz package for deploying a component on a VM.

Application Package Directory Structure

The following uses the Java application package demoJavaExample.zip to illustrate the application package directory structure:

demoJavaExample/
│
├── scripts/
│   │      ├── pre-install.sh
│   │      ├── pre-stop.sh
│   │      ├── stop.sh
│   │      ├── uninstall.sh
│   │      ├── install.sh
│   │      ├── start.sh
│   │      ├── check.sh
│   │      ├── post-start.sh
├── packages/
│   │      ├── weather-1.0.0.jar
├── config/
│   │      ├── system.cfg
├── appspec.yml

The prefix of the package name must be the same as the directory name of the decompressed file. For example, a package named demoJavaExample.zip will have a directory after decompression named demoJavaExample.

The directories and files in the application package are described as follows:

  • scripts: (mandatory) stores script files executed in each application lifecycle.
  • packages: (mandatory) stores application JAR or WAR packages.
  • config: (mandatory) stores application configuration information. For Java applications, the system.cfg file is stored. For Tomcat applications, the system.cfg, logging.properties, and server.xml files are stored.
  • appspec.yml: (mandatory) records the lifecycle definition and also specifies information such as health check.

appspec.yml File Description

As shown in the following, the appspec.yml file defines the entire deployment process, and the environment variables and health check information used during deployment.

spec:
  # Custom user for application running.
  deps:
    - name: "@os/linux/user@1.0"
      user: www
      group: www
      home: /home/www

  # Directly import the environment variables of the application lifecycle script.
  env:
  - name: APP_ENV
    value: "{{app.env}}"

  # Import the environment variables of the application lifecycle script stored in /opt/application/${appName}/${appVersion}/${instanceId}/servicestage-vmapp/application.conf as a file.
  value:
  - name: APP_VALUE
    value: "{{app.value}}"

  # Application health check.
  probes:
    # Interface health check.
    # health:
    #   exec:
    #     method: GET
    #     request: http://127.0.0.1:8080/healthcheck
    #     timeout: 5

    # Script command health check.
    liveness:
      exec:
        command:
          - ps -ef | grep ${APP_HOME}/apache-tomcat-*/bin/bootstrap.jar | grep -v grep   # Currently, only APP_HOME can be used to obtain environment variables.
        timeout: 300
        runas: www
    retries: 10
    interval: 6

  # Application lifecycle script.
  lifecycle:
    pre-install:
      - command: scripts/pre-install.sh
        timeout: 300
    install:
      - command: scripts/install.sh
        timeout: 300
    check:
      - command: scripts/check.sh
        timeout: 300
        runas: www
    start:
      - command: scripts/start.sh
        timeout: 300
        runas: www
    post-start:
      - command: scripts/post-start.sh
        timeout: 300
        runas: www
    pre-stop:
      - command: scripts/pre-stop.sh
        timeout: 300
        runas: www
    stop:
      - command: scripts/stop.sh
        timeout: 300
        runas: www
    uninstall:
      - command: scripts/uninstall.sh
        timeout: 300
  • deps: defines the custom user for application running. Set this field by referring to the following example.
    deps:
        - name: "@os/linux/user@1.0"
          user: www
          group: www
          home: /home/www
  • env: records the environment variables during the running of each script. The environment variables can be directly imported during script running.
  • value: saved in the ${APP_HOME}/servicestage-vmapp/application.conf file. To use these values in the script, run the following command at the beginning of the script:
    #!/bin/bash 
      . ${APP_HOME}/servicestage-vmapp/application.conf
    # dosomething
  • probes: health check mode (health or liveness). If both modes are used, the component is considered alive only if both modes return normal status. If only one mode is used, the component is considered alive only if this mode returns normal status. If neither mode is used, the component is considered alive by default in all scenarios except when stopping or deleting the component.
  • health: accesses the health check interface to check the component status. The component is alive when its status code ranges from 200 to 400 (excluding 400).
    • method: method used to access the interface (HTTP or HTTPS)
    • request: access address.
    • body: request body.
    • timeout: timeout interval, in seconds.
  • liveness: runs a command to check the component status. The component is alive when there is no error message and the command output is not empty.
    • command: command used to check the component status
    • runas: Linux user who runs command
    • timeout: command execution timeout interval, in seconds
  • retries: number of times the health check is attempted (default is 10). An application fails startup after this number of retries, counting from when it starts up.
  • interval: how much time passes before a second health check is attempted (default is 6s). The amount of time increases for each failed attempt, so the time passed is the Interval multiplied by the number of previous retries.
  • lifecycle: script or command executed in each lifecycle.
    • command: script file to be executed. The value must be a file path relative to the ${APP_HOME} directory.
    • timeout: timeout interval, in seconds.
    • runas: user who performs the operation

Lifecycle execution sequence:

  • Deployment sequence: ServiceStage installation technology stack + pre-install.sh -> install.sh -> start.sh -> check.sh -> post-start.sh
  • Upgrade/Rollback sequence: ServiceStage installation technology stack + pre-install.sh -> install.sh -> pre-stop.sh -> stop.sh -> uninstall.sh + ServiceStage uninstallation technology stack -> start.sh -> check.sh ->post-start.sh
  • Deletion sequence: pre-stop.sh -> stop.sh -> uninstall.sh + ServiceStage

config Description

  • system.cfg file
    1. Set the config parameter.

      When creating a component based on a VM, you can set some configuration items by referring to Adding a Configuration Item. ServiceStage stores the configured configuration items in the ${APP_HOME}/config/system.cfg file as key-value pairs. You can also preset some configuration parameters in the config file. Example:

      # Format of the content stored in the system.cfg file
      key1=value1
      key2=value2
    2. Reference the config parameter.

      Example:

      #!/bin/bash 
        . ${APP_HOME}/config/system.cfg 
        . ${APP_HOME}/config/user_config.cfg 
        echo ${key1}
  • server.xml file

    See server.xml Description of Tomcat.

  • logging.properties file

    See logging.properties Description of Tomcat.

packages Directory Description

This folder holds the JAR and WAR packages to be executed when running commands in the script.

scripts Directory Description

This folder stores the scripts executed in each lifecycle of an application.

Default System Configurations

The default system configurations are defined in the ${APP_HOME}/servicestage-vmapp/application.conf file and are not directly displayed in environment variables. The environment variables defined in the application.conf file can be referenced only after the following command is added to the script:

. ${APP_HOME}/servicestage-vmapp/application.conf

Script environment variables that can be directly referenced: APP_HOME, those specified in appspec.yml, and those specified in Adding a Component Environment Variable.

Example file content:

export LOG_PATH=/var/log/application/zqb-4-vm-wqd-2-7f6fbc/3c719644-f9f5-46b4-a06a-61fcf163e5b5
export APP_HOME=/opt/application/zqb-4-vm-wqd-2-7f6fbc/2023.1207.11314/3c719644-f9f5-46b4-a06a-61fcf163e5b5
export TOMCAT_STACK_HOME=/opt/application/zqb-4-vm-wqd-2-7f6fbc/2023.1207.11314/3c719644-f9f5-46b4-a06a-61fcf163e5b5/apache-tomcat-8.5.82
export JRE_STACK_HOME=/opt/application/zqb-4-vm-wqd-2-7f6fbc/2023.1207.11314/3c719644-f9f5-46b4-a06a-61fcf163e5b5/jre1.8
export APP_VALUE="{{app.value}}"
export APP_USER=www
export APP_GROUP=www
  • LOG_PATH: log recording path. Application logs are recorded in the log file in LOG_PATH and named {lifecycle}_app.log.
  • APP_HOME: records the running environment of the current application.
  • TOMCAT_STACK_HOME: home directory of Tomcat.
  • JRE_STACK_HOME: home directory of JRE.
  • APP_VALUE: environment variable specified in appspec.yml.
  • APP_USER: the file's user. You are advised to set this parameter to www. Alternatively, you can also custom the user.
  • APP_GROUP: the file's owner group. You are advised to set this parameter to www. Alternatively, you can also custom the user group.
If you customize the user and user group, run the following commands to grant the custom user the permissions to run the script:
sudo -- /bin/bash -c "chmod -R 755 ${APP_HOME}/scripts/* ${LOG_PATH};chown -R ${APP_USER}:${APP_GROUP} ${APP_HOME}/scripts/start.sh;chown -R ${APP_USER}:${APP_GROUP} ${APP_HOME}/scripts/stop.sh;chown -R ${APP_USER}:${APP_GROUP} ${LOG_PATH} ${JRE_STACK_HOME}/"

Script Writing Description

The directory structure for executing scripts on ServiceStage is:

APP_HOME/
│
├── scripts/
│   │      ├── pre-install.sh
│   │      ├── pre-stop.sh
│   │      ├── stop.sh
│   │      ├── uninstall.sh
│   │      ├── install.sh
│   │      ├── start.sh
│   │      ├── check.sh
│   │      ├── post-start.sh
│   ├──packages/
│   │      ├── my-app.jar/my-app.war
├── config/
│   │      ├── system.cfg
├── servicestage-vmapp/
│   │      ├── application.conf
├── jre1.8
├── apache-tomcat-8.5.82
  • When writing scripts, go to the ${APP_HOME} directory to obtain the locations of other files.
  • To obtain the location of JRE or Tomcat, run the following commands at the beginning of the script. Use ${APP_HOME}/servicestage-vmapp/application.conf to load environment variables and then use ${JRE_STACK_HOME} or ${TOMCAT_STACK_HOME} to obtain the values.
  • ServiceStage reports logs in the ${LOG_PATH} directory. Therefore, record them in the log file in the ${LOG_PATH} directory and name them {lifecycle}_app.log to facilitate subsequent log reporting.
  • It is recommended that the script start with the following lines to obtain environment variables and user configurations, and specify the technology stack home directory, log output path, and current execution address.
    #!/bin/bash
    
    # Obtain user configurations and specify environment variables.
    . ${APP_HOME}/config/system.cfg
    . ${APP_HOME}/servicestage-vmapp/application.conf
    
    # Specify the technology stack home directory.
    JRE_HOME=${JRE_STACK_HOME}
    TOMCAT_HOME=${TOMCAT_STACK_HOME}
    
    # Specify the log output path.
    installLog="${LOG_PATH}/install_app.log"
    
    # Method of printing logs.
    function writeLog()
    {
        msg="$1\n"
        printf "[`date '+%Y-%m-%d %H:%M:%S'`] $msg" | sudo tee -a ${installLog};
    }
    
    writeLog "-----begin!------"
  • If you use a custom user to run the script, replace the www user name in the following statement in the stop.sh script with the custom user name.

    Before:

    ps -u www -ef|grep -v grep|grep ${JRE_HOME}/bin/java|awk '{print $2}

    After:

    ps -u {Your custom running user} -ef|grep -v grep|grep ${JRE_HOME}/bin/java|awk '{print $2}

    Example:

    ps -u app-user -ef|grep -v grep|grep ${JRE_HOME}/bin/java|awk '{print $2}

server.xml Description of Tomcat

  • When creating the Tomcat component, you can prepare the server.xml file in advance and copy the file to the Tomcat directory. (You are advised to perform this step in the install lifecycle.)
    #!/bin/bash
    . ${APP_HOME}/servicestage-vmapp/application.conf 
    cp ${APP_HOME}/conf/server.xml ${TOMCAT_STACK_HOME}/conf/server.xml
  • ServiceStage presets a server.xml file in ${TOMCAT_STACK_HOME}/conf/server.xml. The file contains four placeholders. You need to replace them by referring to the following code:
    #!/bin/bash
    . ${APP_HOME}/servicestage-vmapp/application.conf
    function replacePara()
    {
        sWord=$1
        dWord=$2
        theFile=$3
        if [[ "$sWord" == "" || "$theFile" == "" ]]
        then
            writeLog "[ERROR] ReplacePara has empty param, \$1:$sWord, \$3:$theFile"
            return 1
        fi
    
        if [[ ! -f ${theFile} ]]
        then
            writeLog "[ERROR] File $theFile does not exist."
            return 1
        fi
        count=`grep -c "$sWord" ${theFile}`
        if [[ "${count}" == "0" ]];then
            return 0
        fi
        sed "s#$sWord#${dWord}#g" ${theFile} > ${theFile}.temp
        if [[ "$?" != "0" ]]
        then
            writeLog "[ERROR] Sed command in replacePara error."
            return 1
        fi
        mv -f ${theFile}.temp ${theFile}
    }
    replacePara "@{LOG_FILE_PATH_APP}" "${LOG_FILE_PATH_APP}" ${TOMCAT_HOME}/conf/server.xml
    replacePara "@{http_port}" "${instance_port_port}" ${TOMCAT_HOME}/conf/server.xml
    replacePara "@{server_port}" "${server_port}" ${TOMCAT_HOME}/conf/server.xml
    replacePara "@{APP_PACKAGE_NAME}" "examples.war" ${TOMCAT_HOME}/conf/server.xml
    • @{server_port}: port number of the Tomcat server.
    • @{APP_PACKAGE_NAME}: path of the WAR package relative to ${TOMCAT_STACK_HOME}.
    • @{http_port}: port number of the Tomcat connector.
    • @{LOG_FILE_PATH_APP}: path for printing Tomcat logs.

logging.properties Description of Tomcat

Prepare the logging.properties file and copy it to the Tomcat directory. Example of copying the code:

#!/bin/bash
. ${APP_HOME}/servicestage-vmapp/application.conf 
cp ${APP_HOME}/conf/logging.properties ${TOMCAT_STACK_HOME}/conf/logging.properties

The logging.properties file contains the following information, where @{LOG_FILE_PATH_APP} is the actual log directory.

handlers = 1catalina.org.apache.juli.AsyncFileHandler, 2localhost.org.apache.juli.AsyncFileHandler, 3manager.org.apache.juli.AsyncFileHandler, 4host-manager.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler

.handlers = 1catalina.org.apache.juli.AsyncFileHandler, java.util.logging.ConsoleHandler

1catalina.org.apache.juli.AsyncFileHandler.level = FINE
1catalina.org.apache.juli.AsyncFileHandler.directory = @{LOG_FILE_PATH_APP}
1catalina.org.apache.juli.AsyncFileHandler.prefix = catalina.

2localhost.org.apache.juli.AsyncFileHandler.level = FINE
2localhost.org.apache.juli.AsyncFileHandler.directory = @{LOG_FILE_PATH_APP}
2localhost.org.apache.juli.AsyncFileHandler.prefix = localhost.

3manager.org.apache.juli.AsyncFileHandler.level = FINE
3manager.org.apache.juli.AsyncFileHandler.directory = @{LOG_FILE_PATH_APP}
3manager.org.apache.juli.AsyncFileHandler.prefix = manager.

4host-manager.org.apache.juli.AsyncFileHandler.level = FINE
4host-manager.org.apache.juli.AsyncFileHandler.directory = @{LOG_FILE_PATH_APP}
4host-manager.org.apache.juli.AsyncFileHandler.prefix = host-manager.

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].handlers = 3manager.org.apache.juli.AsyncFileHandler

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].handlers = 4host-manager.org.apache.juli.AsyncFileHandler

FAQ

  • How do I run a script as user root?
    1. Write the content to be executed by user root into the root-install.sh script.
    2. Run the following statement in the install.sh script to be executed. ${ROOT_PASSWORD} is the password of user root, and the content in the double quotation marks after -c is the command to be executed. Environment variables will be lost after you switch user, so add the required environment variables before sh.
      echo "${ROOT_PASSWORD}" | su - root -c "APP_HOME=${APP_HOME} sh root-install.sh"
    3. If you get the error message "su: Permission denied", find the /etc/pam.d/su file and comment out "auth required pam_wheel.so use_uid."