Во многих Java-проектах вы часто видите скрипт оболочки-обертки, который вызывает команду java с ее пользовательскими параметрами приложения. Например, $ ANT_HOME / bin / ant, $ GROOVY_HOME / bin / groovy или даже в нашем планировщике TimeMachine вы увидите $ TIMEMACHINE_HOME / bin / scheduler.sh.
Написание этих скриптов-обёрток скучно и подвержено ошибкам. Большинство проблем связано с настройкой правильного пути к классу для приложения. Если вы работаете над внутренним проектом для компании, то вы можете избежать жестких кодов и изменений вашей среды. Но для проектов с открытым исходным кодом люди должны сделать оболочку более гибкой и универсальной. Большинство из них даже предоставляют .bat-версию. Windows DOS — это действительно жестокий и ограниченный терминал, позволяющий избавиться от потребностей вашего проекта. По этой причине я часто призываю других использовать Cygwin настолько, насколько они могут. По крайней мере, у него есть настоящая оболочка bash для работы. Еще одна распространенная проблема с этими обертками — она может быстро выйти из-под контроля и иметь слишком много дубликатов одинаковых скриптов в каждом месте вашего проекта.
В этой статье я покажу вам сценарий оболочки Java, который я написал. Он прост в использовании и очень гибок для запуска практически любой Java-программы. Давайте сначала посмотрим, как он используется, а затем я распечатаю его содержимое внизу поста.
Представляем скрипт оболочки run-java
Если вы посмотрите на $ TIMEMACHINE_HOME / bin / scheduler.sh, то увидите, что он по очереди вызывает скрипт run-java, который находится в том же каталоге.
DIR=$(dirname $0) SCHEDULER_HOME=$DIR/.. $DIR/run-java -Dscheduler.home="$SCHEDULER_HOME" timemachine.scheduler.tool.SchedulerServer "$@"
Как видите, наш run-java может принимать параметры -D. Не только это, но и опция -cp! Более того, вы можете указать эти параметры даже после основного класса! Это делает run-java повторно переносимым другим сценарием, и при этом он может добавлять дополнительные системные свойства и путь к классам.
Например, TimeMachine поставляется с библиотекой Groovy, поэтому вместо того, чтобы снова загружать ее полный дистрибутив, вы можете просто вызвать Groovy следующим образом.
$TIMEMACHINE_HOME/bin/run-java groovy.ui.GroovyMain test.groovy
Вы можете использовать run-java в любом каталоге, в котором вы находитесь, так что это удобно. Он разрешит свой собственный каталог и автоматически загрузит все jar-файлы в каталог lib. Теперь, если вы хотите, чтобы Groovy запускался с дополнительными jar-файлами, вы можете использовать опцию -cp, например:
$TIMEMACHINE_HOME/bin/run-java -cp "$HOME/apps/my-app/lib/*" groovy.ui.GroovyMain test.groovy
Часто дела пойдут не так, если вы не будете осторожны с Java classpath, но с помощью скрипта run-java вы можете сначала выполнить пробный прогон:
RUN_JAVA_DRY=1 $TIMEMACHINE_HOME/bin/run-java -cp "$HOME/apps/my-app/lib/*" groovy.ui.GroovyMain test.groovy
Вы должны запустить все это в одной строке в командной строке. Он должен распечатать вашу полную команду Java со всеми опциями и аргументами для вас, чтобы проверить.
В сценарии есть много других опций, которые вы можете узнать больше, прочитав комментарии к нему. Текущий скрипт будет работать на любом Linux bash или на терминале Windows Cygwin.
Использование run-java во время разработки с Maven
Приведенные выше примеры предполагают, что вы находитесь в освобожденной структуре проекта, такой как эта
$TIMEMACHINE_HOME +- bin/run-java +- lib/*.jar
Но как насчет разработки? Частым случаем использования является то, что вы хотите иметь возможность запускать ваши последние скомпилированные классы в target / classes без необходимости упаковки или выпуска всего проекта. Вы можете использовать нашу run-java и в этом сценарии. Сначала просто добавьте bin / run-java в свой проект, затем запустите mvn compile dependency: copy-dependencies, который сгенерирует все jar-файлы в target / зависимость. Вот и все. Run-java автоматически обнаружит эти каталоги и создаст правильный путь к классу для запуска вашего основного класса.
Если вы используете Eclipse IDE для разработки, то ваша цель / классы всегда будут обновлены, а run-java может быть отличным украшением для вашего проекта даже для разработки.
Получите скрипт оболочки 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