Help Center/ ServiceStage/ FAQs/ Application Development/ How Do I Package a Java or Tomcat Application?
Updated on 2024-12-16 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-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
in the /opt/application/${appName}/${appVersion}/${instanceId}/servicestage-vmapp/application.conf file
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

  # Application lifecycle script.
  lifecycle:
    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: records either health check command or interface, or both.
  • health: accesses the health check interface to check the component status.
    • method: HTTP method used to access the interface.
    • request: access address.
    • body: request body.
    • timeout: timeout interval, in seconds.
  • liveness: runs a command to check the component status.
    • command: command used to check the status.
    • runas: Linux user who runs the command. You are advised to set this parameter to www or leave it empty.
    • timeout: timeout interval, in seconds.
  • 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 executes the command. Must be www or left empty.

Lifecycle execution sequence:

  • Deployment sequence: ServiceStage installation technology stack + install.sh -> start.sh -> check.sh -> post-start.sh
  • Upgrade/Rollback sequence: ServiceStage installation technology stack + 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 configurations are stored in the ${APP_HOME}/servicestage-vmapp/application.conf file. The file content is:

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.
  • APP_GROUP: the file's owner group.

Default system configurations are not directly displayed in environment variables. The available script environment variables are: APP_HOME, those specified in appspec.yml, and those specified in Adding a Component Environment Variable.

Script Writing Description

The directory structure for executing scripts on ServiceStage is:

APP_HOME/
│
├── scripts/
│   │      ├── 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!------"

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