簡介Java程序的Shell腳本包裝
在許多Java工程中,經(jīng)常會(huì)看到帶有程序自定義參數(shù)調(diào)用Java命令的包裝shell腳本。例如,
$ANT_HOME/bin/ant, $GROOVY_HOME/bin/groovy
,甚至在我們的TimeMachine Scheduler程序中也能見到
$TIMEMACHINE_HOME/bin/scheduler.sh
編寫這些包裝腳本很無聊而且容易出錯(cuò)。大多數(shù)的問題來自為程序設(shè)置正確的classpath。如果你正在為一個(gè)公司開發(fā)內(nèi)部項(xiàng)目的話,那么你有可能遠(yuǎn)離糾結(jié)的路徑以及環(huán)境變量問題。但是對(duì)于開源項(xiàng)目,人們需要使包裝更加靈活和通用。大多數(shù)甚至提供了.bat版本。Windows DOS確實(shí)是個(gè)野蠻且被限制的終端而不能很好的滿足你項(xiàng)目腳本需求。因此,我常鼓勵(lì)別人盡量還是多使用Cygwi。至少它具備一個(gè)真實(shí)的bash shell。其他常見的問題就是這些包裝很快就會(huì)失去控制而且在你的項(xiàng)目各處都會(huì)出現(xiàn)很多冗余腳本。
run-java包裝腳本介紹
如果你看到 $TIMEMACHINE_HOME/bin/scheduler.sh 的代碼,你會(huì)看到它其實(shí)是在同目錄下循環(huán)調(diào)用run-java腳本。
DIR=$(dirname $0) SCHEDULER_HOME=$DIR/.. $DIR/run-java -Dscheduler.home="$SCHEDULER_HOME" timemachine.scheduler.tool.SchedulerServer "$@"
正如你看到的,我們的 run-java 可以使用 -D 選項(xiàng),不僅這樣,它同樣也能使用 -cp 選項(xiàng)!而且,你還能在main class后面指定這些選項(xiàng)!這樣能夠使得run-java被其他的腳本包裝,并且仍舊能夠添加額外的系統(tǒng)屬性以及classpath。
例如,TimeMachine 附帶了 Groovy 庫,所以你可以簡單的像這樣調(diào)用
groovy:$TIMEMACHINE_HOME/bin/run-java groovy.ui.GroovyMain test.groovy
你可以很方便地在任何目錄下使用,它確認(rèn)自己的目錄然后可以自動(dòng)加載lib目錄下的任何jar包?,F(xiàn)在如果你想要附加更多的jar包來運(yùn)行Groovy的話,可以如下使用 -cp 選項(xiàng):
$TIMEMACHINE_HOME/bin/run-java -cp "$HOME/apps/my-app/lib/*" groovy.ui.GroovyMain test.groovy
RUN_JAVA_DRY=1 $TIMEMACHINE_HOME/bin/run-java -cp "$HOME/apps/my-app/lib/*" groovy.ui.GroovyMain test.groovy
你只需在命令提示行中運(yùn)行上面一整行代碼即可。它將輸出完整的附帶所有選項(xiàng)和參數(shù)的java命令。
run-script還包含很多其它的選項(xiàng),你可以通過查看其注釋了解。當(dāng)前的腳本能夠在任何的Linux bash和Windows Cygwin中運(yùn)行。
在開發(fā)中通過Maven使用 run-java
根據(jù)上面提到的示例,假設(shè)項(xiàng)目發(fā)布結(jié)構(gòu)如下:
$TIMEMACHINE_HOME +- bin/run-java +- lib/*.jar
但是在開發(fā)過程中目錄會(huì)是怎樣呢?一個(gè)常見的用例便是:你希望能夠運(yùn)行target/classes下最新編譯的代碼而不是將整個(gè)項(xiàng)目打包或者發(fā)布。你同樣可以在此種情況下使用 run-java 。首先,簡單的將 bin/run-java 添加進(jìn)你的項(xiàng)目,然后運(yùn)行
mvn compile dependency:copy-dependencies
將會(huì)在target/dependency下生成所有的jar文件。就只需要做這些。run-java將自動(dòng)的檢測(cè)這些目錄,并為你的main class創(chuàng)建正確的classpath。
如果你使用Eclipse來開發(fā),那么你的target/classes目錄將總是在更新的,run-java便能成為你項(xiàng)目開發(fā)中的瑰寶。
獲取 run-java 包裝腳本
#!/usr/bin/env bash
#
# Copyright 2012 Zemian Deng
#
# Licensed 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.
# A wrapper script that run any Java6 application in unix/cygwin env.
#
# This script is assumed to be located in an application's "bin" directory. It will
# auto resolve any symbolic link and always run in relative to this application
# directory (which is one parent up from the script.) Therefore, this script can be
# run any where in the file system and it will still reference this application
# directory.
#
# This script will by default auto setup a Java classpath that picks up any "config"
# and "lib" directories under the application directory. It also will also add a
# any typical Maven project output directories such as "target/test-classes",
# "target/classes", and "target/dependency" into classpath. This can be disable by
# setting RUN_JAVA_NO_PARSE=1.
#
# If the "Default parameters" section bellow doesn't match to user's env, then user
# may override these variables in their terminal session or preset them in shell's
# profile startup script. The values of all path should be in cygwin/unix path,
# and this script will auto convert them into Windows path where is needed.
#
# User may customize the Java classpath by setting RUN_JAVA_CP, which will prefix to existing
# classpath, or use the "-cp" option, which will postfix to existing classpath.
#
# Usage:
# run-java [java_opts] <java_main_class> [-cp /more/classpath] [-Dsysprop=value]
#
# Example:
# run-java example.Hello
# run-java example.Hello -Dname=World
# run-java org.junit.runner.JUnitCore example.HelloTest -cp "C:\apps\lib\junit4.8.2\*"
#
# Created by: Zemian Deng 03/09/2012
# This run script dir (resolve to absolute path)
SCRIPT_DIR=$(cd $(dirname $0) && pwd) # This dir is where this script live.
APP_DIR=$(cd $SCRIPT_DIR/.. && pwd) # Assume the application dir is one level up from script dir.
# Default parameters
JAVA_HOME=${JAVA_HOME:=/apps/jdk} # This is the home directory of Java development kit.
RUN_JAVA_CP=${RUN_JAVA_CP:=$CLASSPATH} # A classpath prefix before -classpath option, default to $CLASSPATH
RUN_JAVA_OPTS=${RUN_JAVA_OPTS:=} # Java options (-Xmx512m -XX:MaxPermSize=128m etc)
RUN_JAVA_DEBUG=${RUN_JAVA_DEBUG:=} # If not empty, print the full java command line before executing it.
RUN_JAVA_NO_PARSE=${RUN_JAVA_NO_PARSE:=} # If not empty, skip the auto parsing of -D and -cp options from script arguments.
RUN_JAVA_NO_AUTOCP=${RUN_JAVA_NO_AUTOCP:=} # If not empty, do not auto setup Java classpath
RUN_JAVA_DRY=${RUN_JAVA_DRY:=} # If not empty, do not exec Java command, but just print
# OS specific support. $var _must_ be set to either true or false.
CYGWIN=false;
case "`uname`" in
CYGWIN*) CYGWIN=true ;;
esac
# Define where is the java executable is
JAVA_CMD=java
if [ -d "$JAVA_HOME" ]; then
JAVA_CMD="$JAVA_HOME/bin/java"
fi
# Auto setup applciation's Java Classpath (only if they exists)
if [ -z "$RUN_JAVA_NO_AUTOCP" ]; then
if $CYGWIN; then
# Provide Windows directory conversion
JAVA_HOME_WIN=$(cygpath -aw "$JAVA_HOME")
APP_DIR_WIN=$(cygpath -aw "$APP_DIR")
if [ -d "$APP_DIR_WIN\config" ]; then RUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\config" ; fi
if [ -d "$APP_DIR_WIN\target\test-classes" ]; then RUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\target\test-classes" ; fi
if [ -d "$APP_DIR_WIN\target\classes" ]; then RUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\target\classes" ; fi
if [ -d "$APP_DIR_WIN\target\dependency" ]; then RUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\target\dependency\*" ; fi
if [ -d "$APP_DIR_WIN\lib" ]; then RUN_JAVA_CP="$RUN_JAVA_CP;$APP_DIR_WIN\lib\*" ; fi
else
if [ -d "$APP_DIR/config" ]; then RUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/config" ; fi
if [ -d "$APP_DIR/target/test-classes" ]; then RUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/target/test-classes" ; fi
if [ -d "$APP_DIR/target/classes" ]; then RUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/target/classes" ; fi
if [ -d "$APP_DIR/target/dependency" ]; then RUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/target/dependency/*" ; fi
if [ -d "$APP_DIR/lib" ]; then RUN_JAVA_CP="$RUN_JAVA_CP:$APP_DIR/lib/*" ; fi
fi
fi
# Parse addition "-cp" and "-D" after the Java main class from script arguments
# This is done for convenient sake so users do not have to export RUN_JAVA_CP and RUN_JAVA_OPTS
# saparately, but now they can pass into end of this run-java script instead.
# This can be disable by setting RUN_JAVA_NO_PARSE=1.
if [ -z "$RUN_JAVA_NO_PARSE" ]; then
# Prepare variables for parsing
FOUND_CP=
declare -a NEW_ARGS
IDX=0
# Parse all arguments and look for "-cp" and "-D"
for ARG in "$@"; do
if [[ -n $FOUND_CP ]]; then
if [ "$OS" = "Windows_NT" ]; then
# Can't use cygpath here, because cygpath will auto expand "*", which we do not
# want. User will just have to use OS path when specifying "-cp" option.
#ARG=$(cygpath -w -a $ARG)
RUN_JAVA_CP="$RUN_JAVA_CP;$ARG"
else
RUN_JAVA_CP="$RUN_JAVA_CP:$ARG"
fi
FOUND_CP=
else
case $ARG in
'-cp')
FOUND_CP=1
;;
'-D'*)
RUN_JAVA_OPTS="$RUN_JAVA_OPTS $ARG"
;;
*)
NEW_ARGS[$IDX]="$ARG"
let IDX=$IDX+1
;;
esac
fi
done
# Display full Java command.
if [ -n "$RUN_JAVA_DEBUG" ] || [ -n "$RUN_JAVA_DRY" ]; then
echo "$JAVA_CMD" $RUN_JAVA_OPTS -cp "$RUN_JAVA_CP" "${NEW_ARGS[@]}"
fi
# Run Java Main class using parsed variables
if [ -z "$RUN_JAVA_DRY" ]; then
"$JAVA_CMD" $RUN_JAVA_OPTS -cp "$RUN_JAVA_CP" "${NEW_ARGS[@]}"
fi
else
# Display full Java command.
if [ -n "$RUN_JAVA_DEBUG" ] || [ -n "$RUN_JAVA_DRY" ]; then
echo "$JAVA_CMD" $RUN_JAVA_OPTS -cp "$RUN_JAVA_CP" "$@"
fi
# Run Java Main class
if [ -z "$RUN_JAVA_DRY" ]; then
"$JAVA_CMD" $RUN_JAVA_OPTS -cp "$RUN_JAVA_CP" "$@"
fi
fi
相關(guān)文章
Java的SpringMVC中控制器返回XML數(shù)據(jù)問題
這篇文章主要介紹了Java的SpringMVC中控制器返回XML數(shù)據(jù)問題,控制器是處理HTTP請(qǐng)求的組件,它們接收來自客戶端的請(qǐng)求,并將其轉(zhuǎn)換為適當(dāng)?shù)捻憫?yīng),這些響應(yīng)可以是動(dòng)態(tài)生成的?HTML?頁面,也可以是JSON或XML格式的數(shù)據(jù),需要的朋友可以參考下2023-07-07
maven打包時(shí)候修改包名稱帶上git版本號(hào)和打包時(shí)間方式
這篇文章主要介紹了maven打包時(shí)候修改包名稱帶上git版本號(hào)和打包時(shí)間方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
Java中優(yōu)先隊(duì)列PriorityQueue常用方法示例
這篇文章主要介紹了Java中優(yōu)先隊(duì)列PriorityQueue常用方法示例,PriorityQueue是一種特殊的隊(duì)列,滿足隊(duì)列的“隊(duì)尾進(jìn)、隊(duì)頭出”條件,但是每次插入或刪除元素后,都對(duì)隊(duì)列進(jìn)行調(diào)整,使得隊(duì)列始終構(gòu)成最小堆(或最大堆),需要的朋友可以參考下2023-09-09
Java設(shè)置PDF有序和無序列表的知識(shí)點(diǎn)總結(jié)
在本篇文章中小編給大家整理了關(guān)于Java設(shè)置PDF有序和無序列表的知識(shí)點(diǎn),需要的朋友們參考下。2019-03-03
詳解利用SpringMVC攔截器控制Controller返回值
這篇文章主要介紹了詳解利用SpringMVC攔截器控制Controller返回值,通過定義一個(gè)StringResult注解,在訪問方法的時(shí)候返回StringResult中的內(nèi)容,有興趣的可以了解一下。2017-01-01
Springboot+SpringSecurity+JWT實(shí)現(xiàn)用戶登錄和權(quán)限認(rèn)證示例
這篇文章主要介紹了Springboot+SpringSecurity+JWT實(shí)現(xiàn)用戶登錄和權(quán)限認(rèn)證示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
java實(shí)現(xiàn)求兩個(gè)字符串最長公共子串的方法
這篇文章主要介紹了java實(shí)現(xiàn)求兩個(gè)字符串最長公共子串的方法,是一道華為OJ上的一道題目,涉及Java針對(duì)字符串的遍歷、轉(zhuǎn)換及流程控制等技巧,需要的朋友可以參考下2015-12-12
生成PDF全攻略之在已有PDF上添加內(nèi)容的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄蒔DF全攻略之在已有PDF上添加內(nèi)容的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06

