You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bookkeeper.apache.org by hs...@apache.org on 2021/10/22 00:20:14 UTC

[bookkeeper] branch master updated: Issue:2840 Create bookie shellscript for gradle (#2841)

This is an automated email from the ASF dual-hosted git repository.

hsaputra pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/bookkeeper.git


The following commit(s) were added to refs/heads/master by this push:
     new 919fdd3  Issue:2840 Create bookie shellscript for gradle (#2841)
919fdd3 is described below

commit 919fdd39856512b98aab53ee72332a81a1317bea
Author: Prashant Kumar <65...@users.noreply.github.com>
AuthorDate: Thu Oct 21 17:20:07 2021 -0700

    Issue:2840 Create bookie shellscript for gradle (#2841)
    
    Co-authored-by: Prashant Kumar <pr...@splunk.com>
---
 bin/bookkeeper_gradle          | 182 +++++++++++++++++++++
 bin/common_gradle.sh           | 351 +++++++++++++++++++++++++++++++++++++++++
 bookkeeper-server/build.gradle |   7 +
 3 files changed, 540 insertions(+)

diff --git a/bin/bookkeeper_gradle b/bin/bookkeeper_gradle
new file mode 100755
index 0000000..22545b2
--- /dev/null
+++ b/bin/bookkeeper_gradle
@@ -0,0 +1,182 @@
+#!/usr/bin/env bash
+#
+#/**
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements.  See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership.  The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License.  You may obtain a copy of the License at
+# *
+# *     http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+set -e
+
+BINDIR=`dirname "$0"`
+BK_HOME=`cd ${BINDIR}/..;pwd`
+
+source ${BK_HOME}/bin/common_gradle.sh
+
+# default variables
+DEFAULT_CONF=${BK_HOME}/conf/bk_server.conf
+DEFAULT_ZK_CONF=${BK_HOME}/conf/zookeeper.conf
+
+if [ -z "$BOOKIE_CONF" ]; then
+  BOOKIE_CONF_TO_CHECK=${DEFAULT_CONF}
+else
+  BOOKIE_CONF_TO_CHECK=${BOOKIE_CONF}
+fi
+
+FIND_TABLE_SERVICE_RESULT=$(find_table_service ${BOOKIE_CONF_TO_CHECK} $1)
+
+if [ "x${FIND_TABLE_SERVICE_RESULT}" == "xtrue" ]; then
+  BOOKIE_MODULE_PATH=stream/server
+  BOOKIE_MODULE_NAME=${TABLE_SERVICE_MODULE_NAME}
+elif [ "x${FIND_TABLE_SERVICE_RESULT}" == "xfalse" ]; then
+  BOOKIE_MODULE_PATH=bookkeeper-server
+  BOOKIE_MODULE_NAME=${BOOKIE_SERVER_MODULE_NAME}
+else
+  echo ${FIND_TABLE_SERVICE_RESULT}
+  exit 1
+fi
+
+# find the module jar
+BOOKIE_JAR=$(find_module_jar ${BOOKIE_MODULE_PATH} ${BOOKIE_MODULE_NAME})
+
+# set up the classpath
+BOOKIE_CLASSPATH=$(set_module_classpath ${BOOKIE_MODULE_PATH})
+
+bookkeeper_help() {
+    cat <<EOF
+Usage: bookkeeper <command>
+where command is one of:
+
+[service commands]
+
+    bookie              Run a bookie server
+    autorecovery        Run AutoRecovery service daemon
+    zookeeper           Run zookeeper server
+
+[development commands]
+
+    localbookie <n>     Run a test ensemble of <n> bookies locally
+    standalone          Run a standalone cluster (with all service components) locally
+
+[tooling commands]
+
+    upgrade             Upgrade bookie filesystem
+    shell               Run shell for admin commands
+
+[other commands]
+
+    help                This help message
+
+or command is the full name of a class with a defined main() method.
+
+Environment variables:
+   BOOKIE_LOG_CONF        Log4j configuration file (default ${DEFAULT_LOG_CONF})
+   BOOKIE_CONF            Configuration file (default: ${DEFAULT_CONF})
+   BOOKIE_ZK_CONF         Configuration file for zookeeper (default: $DEFAULT_ZK_CONF)
+   BOOKIE_EXTRA_OPTS      Extra options to be passed to the jvm
+   BOOKIE_EXTRA_CLASSPATH Add extra paths to the bookkeeper classpath
+   ENTRY_FORMATTER_CLASS  Entry formatter class to format entries.
+   BOOKIE_PID_DIR         Folder where the Bookie server PID file should be stored
+   BOOKIE_STOP_TIMEOUT    Wait time before forcefully kill the Bookie server instance, if the stop is not successful
+
+These variable can also be set in conf/bkenv.sh
+EOF
+}
+
+# if no args specified, show usage
+if [ $# = 0 ]; then
+  bookkeeper_help;
+  exit 1;
+fi
+
+# get arguments
+COMMAND=$1
+shift
+
+LOCALBOOKIES_CONFIG_DIR="${LOCALBOOKIES_CONFIG_DIR:-/tmp/localbookies-config}"
+if [ ${COMMAND} == "shell" ]; then
+  DEFAULT_LOG_CONF=${BK_HOME}/conf/log4j.shell.properties
+  if [[ $1 == "-localbookie"  ]]; then
+    if [[ $2 == *:* ]];
+    then
+      BOOKIE_CONF=${LOCALBOOKIES_CONFIG_DIR}/$2.conf
+      shift 2
+    else
+      BOOKIE_CONF=${LOCALBOOKIES_CONFIG_DIR}/baseconf.conf
+      shift
+    fi
+  fi
+fi
+
+if [ -z "$BOOKIE_ZK_CONF" ]; then
+    BOOKIE_ZK_CONF=$DEFAULT_ZK_CONF
+fi
+
+if [ -z "$BOOKIE_CONF" ]; then
+  BOOKIE_CONF=${DEFAULT_CONF}
+fi
+
+# Configure logging
+if [ -z "$BOOKIE_LOG_CONF" ]; then
+  BOOKIE_LOG_CONF=${DEFAULT_LOG_CONF}
+fi
+BOOKIE_LOG_DIR=${BOOKIE_LOG_DIR:-"$BK_HOME/logs"}
+BOOKIE_LOG_FILE=${BOOKIE_LOG_FILE:-"bookkeeper-server.log"}
+BOOKIE_ROOT_LOGGER=${BOOKIE_ROOT_LOGGER:-"INFO,CONSOLE"}
+
+# Configure the classpath
+BOOKIE_CLASSPATH="$BOOKIE_JAR:$BOOKIE_CLASSPATH:$BOOKIE_EXTRA_CLASSPATH"
+BOOKIE_CLASSPATH="`dirname $BOOKIE_LOG_CONF`:$BOOKIE_CLASSPATH"
+
+# Build the OPTS
+BOOKIE_OPTS=$(build_bookie_opts)
+GC_OPTS=$(build_bookie_jvm_opts ${BOOKIE_LOG_DIR} "gc_%p.log")
+NETTY_OPTS=$(build_netty_opts)
+LOGGING_OPTS=$(build_logging_opts ${BOOKIE_LOG_CONF} ${BOOKIE_LOG_DIR} ${BOOKIE_LOG_FILE} ${BOOKIE_ROOT_LOGGER})
+
+BOOKIE_EXTRA_OPTS="${BOOKIE_EXTRA_OPTS} -Dorg.bouncycastle.fips.approved_only=true"
+OPTS="${OPTS} -cp ${BOOKIE_CLASSPATH} ${BOOKIE_OPTS} ${GC_OPTS} ${NETTY_OPTS} ${LOGGING_OPTS} ${BOOKIE_EXTRA_OPTS}"
+
+# Create log dir if it doesn't exist
+if [ ! -d ${BOOKIE_LOG_DIR} ]; then
+    mkdir ${BOOKIE_LOG_DIR}
+fi
+
+#Change to BK_HOME to support relative paths
+cd "$BK_HOME"
+if [ ${COMMAND} == "bookie" ]; then
+  exec "${JAVA}" ${OPTS} ${JMX_ARGS} org.apache.bookkeeper.server.Main --conf ${BOOKIE_CONF} $@
+elif [ ${COMMAND} == "autorecovery" ]; then
+  exec "${JAVA}" ${OPTS} ${JMX_ARGS} org.apache.bookkeeper.replication.AutoRecoveryMain --conf ${BOOKIE_CONF} $@
+elif [ ${COMMAND} == "localbookie" ]; then
+  NUMBER=$1
+  shift
+  exec "${JAVA}" ${OPTS} ${JMX_ARGS} -Dzookeeper.4lw.commands.whitelist='*' org.apache.bookkeeper.util.LocalBookKeeper ${NUMBER} ${BOOKIE_CONF} $@
+elif [ ${COMMAND} == "standalone" ]; then
+  exec "${JAVA}" ${OPTS} ${JMX_ARGS} -Dzookeeper.4lw.commands.whitelist='*' org.apache.bookkeeper.stream.cluster.StandaloneStarter --conf ${BK_HOME}/conf/standalone.conf $@
+elif [ ${COMMAND} == "upgrade" ]; then
+  exec "${JAVA}" ${OPTS} org.apache.bookkeeper.bookie.FileSystemUpgrade --conf ${BOOKIE_CONF} $@
+elif [ $COMMAND == "zookeeper" ]; then
+    BOOKIE_LOG_FILE=${BOOKIE_LOG_FILE:-"zookeeper.log"}
+    exec "${JAVA}" $OPTS -Dbookkeeper.log.file=$BOOKIE_LOG_FILE org.apache.zookeeper.server.quorum.QuorumPeerMain $BOOKIE_ZK_CONF $@
+elif [ ${COMMAND} == "shell" ]; then
+  ENTRY_FORMATTER_ARG="-DentryFormatterClass=${ENTRY_FORMATTER_CLASS:-org.apache.bookkeeper.util.StringEntryFormatter}"
+  exec "${JAVA}" ${OPTS} ${ENTRY_FORMATTER_ARG} org.apache.bookkeeper.bookie.BookieShell -conf ${BOOKIE_CONF} $@
+elif [ ${COMMAND} == "help" ]; then
+  bookkeeper_help;
+else
+  exec "${JAVA}" ${OPTS} ${COMMAND} $@
+fi
+
diff --git a/bin/common_gradle.sh b/bin/common_gradle.sh
new file mode 100755
index 0000000..9910d8c
--- /dev/null
+++ b/bin/common_gradle.sh
@@ -0,0 +1,351 @@
+#!/usr/bin/env bash
+#/**
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements.  See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership.  The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License.  You may obtain a copy of the License at
+# *
+# *     http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+# Check net.ipv6.bindv6only
+if [ -f /sbin/sysctl ] && [ -f /proc/sys/net/ipv6/bindv6only ]; then
+  # check if net.ipv6.bindv6only is set to 1
+  bindv6only=$(/sbin/sysctl -n net.ipv6.bindv6only 2> /dev/null)
+  if [ -n "$bindv6only" ] && [ "$bindv6only" -eq "1" ]
+  then
+    echo "Error: \"net.ipv6.bindv6only\" is set to 1 - Java networking could be broken"
+    echo "For more info (the following page also applies to bookkeeper): http://wiki.apache.org/hadoop/HadoopIPv6"
+    exit 1
+  fi
+fi
+
+# See the following page for extensive details on setting
+# up the JVM to accept JMX remote management:
+# http://java.sun.com/javase/6/docs/technotes/guides/management/agent.html
+# by default we allow local JMX connections
+if [ "x$JMXLOCALONLY" = "x" ]
+then
+  JMXLOCALONLY=false
+fi
+
+if [ "x$JMXDISABLE" = "x" ]
+then
+  # for some reason these two options are necessary on jdk6 on Ubuntu
+  #   accord to the docs they are not necessary, but otw jconsole cannot
+  #   do a local attach
+  JMX_ARGS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY"
+else
+  echo "JMX disabled by user request" >&2
+fi
+
+# Check for the java to use
+if [[ -z ${JAVA_HOME} ]]; then
+  JAVA=$(which java)
+  if [ $? = 0 ]; then
+    echo "JAVA_HOME not set, using java from PATH. ($JAVA)"
+  else
+    echo "Error: JAVA_HOME not set, and no java executable found in $PATH." 1>&2
+    exit 1
+  fi
+else
+  JAVA=${JAVA_HOME}/bin/java
+fi
+
+BINDIR=${BK_BINDIR:-"`dirname "$0"`"}
+BK_HOME=${BK_HOME:-"`cd ${BINDIR}/..;pwd`"}
+BK_CONFDIR=${BK_HOME}/conf
+DEFAULT_LOG_CONF=${BK_CONFDIR}/log4j.properties
+
+source ${BK_CONFDIR}/nettyenv.sh
+source ${BK_CONFDIR}/bkenv.sh
+source ${BK_CONFDIR}/bk_cli_env.sh
+
+detect_jdk8() {
+
+  if [ -f "$JAVA_HOME/lib/modules" ]; then
+     echo "0"
+  else
+     echo "1"
+  fi
+  return
+}
+
+# default netty settings
+NETTY_LEAK_DETECTION_LEVEL=${NETTY_LEAK_DETECTION_LEVEL:-"disabled"}
+NETTY_RECYCLER_MAXCAPACITY=${NETTY_RECYCLER_MAXCAPACITY:-"1000"}
+NETTY_RECYCLER_LINKCAPACITY=${NETTY_RECYCLER_LINKCAPACITY:-"1024"}
+
+USING_JDK8=$(detect_jdk8)
+
+if [ "$USING_JDK8" -ne "1" ]; then
+   DEFAULT_BOOKIE_GC_OPTS="-XX:+UseG1GC \
+    -XX:MaxGCPauseMillis=10 \
+    -XX:+ParallelRefProcEnabled \
+    -XX:+DisableExplicitGC"
+   DEFAULT_BOOKIE_GC_LOGGING_OPTS=""
+else
+  DEFAULT_BOOKIE_GC_OPTS="-XX:+UseG1GC \
+    -XX:MaxGCPauseMillis=10 \
+    -XX:+ParallelRefProcEnabled \
+    -XX:+UnlockExperimentalVMOptions \
+    -XX:+AggressiveOpts \
+    -XX:+DoEscapeAnalysis \
+    -XX:ParallelGCThreads=32 \
+    -XX:ConcGCThreads=32 \
+    -XX:G1NewSizePercent=50 \
+    -XX:+DisableExplicitGC \
+    -XX:-ResizePLAB"
+  DEFAULT_BOOKIE_GC_LOGGING_OPTS="-XX:+PrintGCDetails \
+    -XX:+PrintGCApplicationStoppedTime  \
+    -XX:+UseGCLogFileRotation \
+    -XX:NumberOfGCLogFiles=5 \
+    -XX:GCLogFileSize=64m"
+fi
+
+BOOKIE_MAX_HEAP_MEMORY=${BOOKIE_MAX_HEAP_MEMORY:-"1g"}
+BOOKIE_MIN_HEAP_MEMORY=${BOOKIE_MIN_HEAP_MEMORY:-"1g"}
+BOOKIE_MAX_DIRECT_MEMORY=${BOOKIE_MAX_DIRECT_MEMORY:-"2g"}
+BOOKIE_MEM_OPTS=${BOOKIE_MEM_OPTS:-"-Xms${BOOKIE_MIN_HEAP_MEMORY} -Xmx${BOOKIE_MAX_HEAP_MEMORY} -XX:MaxDirectMemorySize=${BOOKIE_MAX_DIRECT_MEMORY}"}
+BOOKIE_GC_OPTS=${BOOKIE_GC_OPTS:-"${DEFAULT_BOOKIE_GC_OPTS}"}
+BOOKIE_GC_LOGGING_OPTS=${BOOKIE_GC_LOGGING_OPTS:-"${DEFAULT_BOOKIE_GC_LOGGING_OPTS}"}
+
+# default CLI JVM settings
+DEFAULT_CLI_GC_OPTS="-XX:+UseG1GC \
+    -XX:MaxGCPauseMillis=10"
+if [ "$USING_JDK8" -ne "1" ]; then
+  DEFAULT_CLI_GC_LOGGING_OPTS=""
+else
+  DEFAULT_CLI_GC_LOGGING_OPTS="-XX:+PrintGCDetails \
+    -XX:+PrintGCApplicationStoppedTime  \
+    -XX:+UseGCLogFileRotation \
+    -XX:NumberOfGCLogFiles=5 \
+    -XX:GCLogFileSize=64m"
+fi
+
+CLI_MAX_HEAP_MEMORY=${CLI_MAX_HEAP_MEMORY:-"512M"}
+CLI_MIN_HEAP_MEMORY=${CLI_MIN_HEAP_MEMORY:-"256M"}
+CLI_MEM_OPTS=${CLI_MEM_OPTS:-"-Xms${CLI_MIN_HEAP_MEMORY} -Xmx${CLI_MAX_HEAP_MEMORY}"}
+CLI_GC_OPTS=${CLI_GC_OPTS:-"${DEFAULT_CLI_GC_OPTS}"}
+CLI_GC_LOGGING_OPTS=${CLI_GC_LOGGING_OPTS:-"${DEFAULT_CLI_GC_LOGGING_OPTS}"}
+
+# module names
+BOOKIE_SERVER_MODULE_NAME="bookkeeper-server"
+TABLE_SERVICE_MODULE_NAME="stream-storage-server"
+
+is_released_binary() {
+  if [ -d ${BK_HOME}/lib ]; then
+    echo "true"
+    return
+  else
+    echo "false"
+    return
+  fi
+}
+
+find_module_jar_at() {
+  DIR=$1
+  MODULE=$2
+  REGEX="^${MODULE}.jar$"
+  if [ -d ${DIR} ]; then
+    cd ${DIR}
+    for f in *.jar; do
+      if [[ ${f} =~ ${REGEX} ]]; then
+        echo ${DIR}/${f}
+        return
+      fi
+    done
+  fi
+}
+
+find_module_release_jar() {
+  MODULE_NAME=$1
+  RELEASE_JAR=$(find_module_jar_at ${BK_HOME} ${MODULE_NAME})
+  if [ -n "${RELEASE_JAR}" ]; then
+    MODULE_JAR=${RELEASE_JAR}
+  else
+    RELEASE_JAR=$(find_module_jar_at ${BK_HOME}/lib ${MODULE_NAME})
+    if [ -n "${RELEASE_JAR}" ]; then
+      MODULE_JAR=${RELEASE_JAR}
+    fi
+  fi
+  echo ${RELEASE_JAR}
+  return
+}
+
+find_module_jar() {
+  MODULE_PATH=$1
+  MODULE_NAME=$2
+  RELEASE_JAR=$(find_module_jar_at ${BK_HOME} ${MODULE_NAME})
+  if [ -n "${RELEASE_JAR}" ]; then
+    MODULE_JAR=${RELEASE_JAR}
+  else
+    RELEASE_JAR=$(find_module_jar_at ${BK_HOME}/lib ${MODULE_NAME})
+    if [ -n "${RELEASE_JAR}" ]; then
+      MODULE_JAR=${RELEASE_JAR}
+    fi
+  fi
+
+  if [ -z "${MODULE_JAR}" ]; then
+    BUILT_JAR=$(find_module_jar_at ${BK_HOME}/${MODULE_PATH}/build/libs ${MODULE_NAME})
+    if [ -z "${BUILT_JAR}" ]; then
+      echo "${BK_HOME}/${MODULE_PATH}/build/libs" >&2;
+      echo "Couldn't find module '${MODULE_NAME}' jar." >&2
+     ## read -p "Do you want me to run \`mvn package -DskipTests\` for you ? (y|n) " answer
+
+      BUILT_JAR=$(find_module_jar_at ${BK_HOME}/${MODULE_PATH}/build/libs ${MODULE_NAME})
+    fi
+    if [ -n "${BUILT_JAR}" ]; then
+      MODULE_JAR=${BUILT_JAR}
+    fi
+  fi
+
+  if [ ! -e "${MODULE_JAR}" ]; then
+    echo "Could not find module '${MODULE_JAR}' jar." >&2
+    exit 1
+  fi
+  ##echo ${MODULE_JAR}
+  return
+}
+
+add_maven_deps_to_classpath() {
+  MODULE_PATH=$1
+  MVN="mvn"
+  if [ "$MAVEN_HOME" != "" ]; then
+    MVN=${MAVEN_HOME}/bin/mvn
+  fi
+
+  # Need to generate classpath from maven pom. This is costly so generate it
+  # and cache it. Save the file into our target dir so a mvn clean will get
+  # clean it up and force us create a new one.
+  f="${BK_HOME}/${MODULE_PATH}/build/classpath.txt"
+  if [ ! -f ${f} ]; then
+    echo "the classpath of module '${MODULE_PATH}' is not found, generating it ..." >&2
+    ${BK_HOME}/gradlew ${MODULE_PATH}:jar
+    echo "the classpath of module '${MODULE_PATH}' is generated at '${f}'." >&2
+  fi
+}
+
+set_module_classpath() {
+  MODULE_PATH=$1
+  if [ -d "${BK_HOME}/lib" ]; then
+    BK_CLASSPATH=""
+    for i in ${BK_HOME}/lib/*.jar; do
+      BK_CLASSPATH=${BK_CLASSPATH}:${i}
+    done
+    echo ${BK_CLASSPATH}
+  else
+    add_maven_deps_to_classpath ${MODULE_PATH} >&2
+    cat ${BK_HOME}/${MODULE_PATH}/build/classpath.txt
+  fi
+  return
+}
+
+build_bookie_jvm_opts() {
+  LOG_DIR=$1
+  GC_LOG_FILENAME=$2
+  if [ "$USING_JDK8" -eq "1" ]; then
+    echo "$BOOKIE_MEM_OPTS $BOOKIE_GC_OPTS $BOOKIE_GC_LOGGING_OPTS $BOOKIE_PERF_OPTS -Xloggc:${LOG_DIR}/${GC_LOG_FILENAME}"
+  else
+    echo "$BOOKIE_MEM_OPTS $BOOKIE_GC_OPTS $BOOKIE_GC_LOGGING_OPTS $BOOKIE_PERF_OPTS -Xlog:gc=info:file=${LOG_DIR}/${GC_LOG_FILENAME}::filecount=5,filesize=64m"
+  fi
+  return
+}
+
+build_cli_jvm_opts() {
+  LOG_DIR=$1
+  GC_LOG_FILENAME=$2
+  if [ "$USING_JDK8" -eq "1" ]; then
+    echo "$CLI_MEM_OPTS $CLI_GC_OPTS $CLI_GC_LOGGING_OPTS -Xloggc:${LOG_DIR}/${GC_LOG_FILENAME}"
+  else
+    echo "$CLI_MEM_OPTS $CLI_GC_OPTS $CLI_GC_LOGGING_OPTS -Xlog:gc=info:file=${LOG_DIR}/${GC_LOG_FILENAME}::filecount=5,filesize=64m"
+  fi
+  return
+}
+
+build_netty_opts() {
+  echo "-Dio.netty.leakDetectionLevel=${NETTY_LEAK_DETECTION_LEVEL} \
+    -Dio.netty.recycler.maxCapacity.default=${NETTY_RECYCLER_MAXCAPACITY} \
+    -Dio.netty.recycler.linkCapacity=${NETTY_RECYCLER_LINKCAPACITY}"
+}
+
+build_logging_opts() {
+  CONF_FILE=$1
+  LOG_DIR=$2
+  LOG_FILE=$3
+  LOGGER=$4
+
+  echo "-Dlog4j.configuration=`basename ${CONF_FILE}` \
+    -Dbookkeeper.root.logger=${LOGGER} \
+    -Dbookkeeper.log.dir=${LOG_DIR} \
+    -Dbookkeeper.log.file=${LOG_FILE}"
+}
+
+build_cli_logging_opts() {
+  CONF_FILE=$1
+  LOG_DIR=$2
+  LOG_FILE=$3
+  LOGGER=$4
+
+  echo "-Dlog4j.configuration=`basename ${CONF_FILE}` \
+    -Dbookkeeper.cli.root.logger=${LOGGER} \
+    -Dbookkeeper.cli.log.dir=${LOG_DIR} \
+    -Dbookkeeper.cli.log.file=${LOG_FILE}"
+}
+
+build_bookie_opts() {
+  echo "-Djava.net.preferIPv4Stack=true"
+}
+
+find_table_service() {
+  BOOKIE_CONF_TO_CHECK=$1
+  SERVICE_COMMAND=$2
+
+  # check if it is a released binary
+  IS_RELEASED_BINARY=$(is_released_binary)
+
+  # check if table service is released
+  TABLE_SERVICE_RELEASED="true"
+  if [ "x${IS_RELEASED_BINARY}" == "xtrue" ]; then
+    TABLE_SERVICE_RELEASE_JAR=$(find_module_release_jar ${TABLE_SERVICE_MODULE_NAME})
+    if [ "x${TABLE_SERVICE_RELEASE_JAR}" == "x" ]; then
+      TABLE_SERVICE_RELEASED="false"
+    fi
+  fi
+  
+  # check the configuration to see if table service is enabled or not.
+  if [ -z "${ENABLE_TABLE_SERVICE}" ]; then
+    # mask exit code if the configuration file doesn't contain `StreamStorageLifecycleComponent`
+    TABLE_SERVICE_SETTING=$(grep StreamStorageLifecycleComponent ${BOOKIE_CONF_TO_CHECK} | cat)
+    if [[ "${TABLE_SERVICE_SETTING}" =~ ^extraServerComponents.* ]]; then
+      if [ "x${TABLE_SERVICE_RELEASED}" == "xfalse" ]; then
+        echo "The release binary is built without table service. Please disable \`StreamStorageLifecycleComponent\` in your bookie configuration at '${BOOKIE_CONF_TO_CHECK}'."
+        return
+      fi
+      ENABLE_TABLE_SERVICE="true"
+    fi
+  fi
+  
+  # standalone only run
+  if [ \( "x${SERVICE_COMMAND}" == "xstandalone" \) -a \( "x${TABLE_SERVICE_RELEASED}" == "xfalse" \) ]; then
+    echo "The release binary is built without table service. Use \`localbookie <n>\` instead of \`standalone\` for local development."
+    return
+  fi
+
+  if [ \( "x${SERVICE_COMMAND}" == "xstandalone" \) -o \( "x${ENABLE_TABLE_SERVICE}" == "xtrue" \) ]; then
+    echo "true"
+    return
+  else
+    echo "false"
+    return
+  fi
+}
diff --git a/bookkeeper-server/build.gradle b/bookkeeper-server/build.gradle
index 1445fbe..bdfccc3 100644
--- a/bookkeeper-server/build.gradle
+++ b/bookkeeper-server/build.gradle
@@ -56,6 +56,8 @@ dependencies {
     implementation depLibs.log4jSlf4jImpl
     implementation depLibs.log4j12api
     implementation depLibs.log4jCore
+    runtimeOnly depLibs.metricsCore
+    runtimeOnly depLibs.snappy
 
     testImplementation project(':bookkeeper-stats-providers:prometheus-metrics-provider')
     testImplementation project(':bookkeeper-http:vertx-http-server')
@@ -77,6 +79,11 @@ dependencies {
     testAnnotationProcessor depLibs.lombok
 }
 
+task writeClasspath {
+    buildDir.mkdirs()
+    new File(buildDir, "classpath.txt").text = sourceSets.main.runtimeClasspath.collect { it.absolutePath }.join(':') + "\n"
+}
+
 test {
     retry {
         maxFailures = 200