多個平臺的 Java Launcher 腳本

一般對於服務來講,過長的命令每每讓人感到厭煩,人們須要的只是簡單的操做,而且可以支持複雜的功能,對於 Java 開發的服務來講更是如此。
一個比較複雜的 Jar 服務使用Java啓動,命令以下java

java -Xms512m -Xmx512m -jar fuck.jar --config config.server -port 10086android

實際上許多虛擬機的語言的 Host 命令格式也是相似的。
咱們分析能夠知道對於基於虛擬機的語言,命令行基本上是 host+vm 運行參數+執行文件路徑+輸入參數。 固然若是參數較少,咱們徹底不用寫一個 Launcher 腳原本管理服務。git

Launcher 腳本須要提供的命令至少有:shell

  1. start
  2. stop
  3. restart
  4. status
  5. help

##實現 在 Linux 系統上,啓動腳本應該是簡單的,不準要過多依賴的,通常而言推薦使用 shell 腳本,實際上不少軟件在 Linux 上的 Launcher 都是使用 Shell 語言。android studio,brackets Codebox ,甚至 Chrome,Firefox 都有 shell 腳本的啓動器。
在 Windows 上早期,部分軟件使用 cmd 來寫啓動器,然而 cmd 的功能孱弱,微軟適時的推出了 PowerShell,PowerShell 在功能上很是強大,甚至要優於Shell,因此採用 PowerShell 來寫啓動器,而且寫一個 cmd 輔助腳本啓動PowerShell。函數

@echo off

if not exist "%~dp0launcher.ps1" goto NotFound
PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0launcher.ps1' %*"
goto :EOF
:NotFound
echo Not Found launcher.ps1 in %~dp0,Please reset your launcher
PAUSE

###配置文件的讀取 選擇合適的配置文件可以簡化操做,對於簡單的 Shell 腳本而言,複雜的配置文件是難以實現的。我將 Launcher 的配置文件分爲兩類,一個是 JVM 的參數,也就是上面的 "-Xms512m -Xmx512m" 這種文件的格式就是按行讀取,行首存在‘#’字符就拋棄。 另外一類是基於 INI-Style 的配置文件,主要是 JDK 的路徑,須要運行的 jar 包的路徑,以及重定向的設置,因爲 Windows 和 Linux 的文件系統存在差別,因此在涉及到文件系統的設置遷移到了 Windows 和 Posix 節.net

Bash 解析 Ini 文件:命令行

function GetPrivateProfileString()
{
	if [ ! -f $1 ] || [ $# -ne 3 ];then
		return 1
	fi
	blockname=$2
	fieldname=$3

	begin_block=0
	end_block=0

	cat $1 | while read line
	do

		if [ "X$line" = "X[$blockname]" ];then
			begin_block=1
			continue
		fi

		if [ $begin_block -eq 1 ];then
			end_block=$(echo $line | awk 'BEGIN{ret=0} /^\[.*\]$/{ret=1} END{print ret}')
			if [ $end_block -eq 1 ];then
				#echo "end block"
				break
			fi

			need_ignore=$(echo $line | awk 'BEGIN{ret=0} /^#/{ret=1} /^$/{ret=1} END{print ret}')
			if [ $need_ignore -eq 1 ];then
				#echo "ignored line:" $line
				continue
			fi
			field=$(echo $line | awk -F= '{gsub(" |\t","",$1); print $1}')
			#####Fix Me We Support Space Value
			value=$(echo $line | awk -F= '{gsub("","",$2); print $2}')
			#echo "'$field':'$value'"
			if [ "X$fieldname" = "X$field" ];then
				#echo "result value:'$result'"
				echo $value
				break
			fi

		fi
	done
	return 0
}

PowerShell 解析 Ini 文件:rest

Function Parser-IniFile
{
    param(
        [Parameter(Position=0,Mandatory=$True,HelpMessage="Enter Your Ini File Path")]
        [ValidateNotNullorEmpty()]
        [String]$File
        )
    $ini = @{}
    $section = "NO_SECTION"
    $ini[$section] = @{}
    switch -regex -file $File {
        "^\[(.+)\]$" {
            $section = $matches[1].Trim()
            $ini[$section] = @{}
        }
        "^\s*([^#].+?)\s*=\s*(.*)" {
            $name,$value = $matches[1..2]
            # skip comments that start with semicolon:
            if (!($name.StartsWith(";"))) {
                $ini[$section][$name] = $value.Trim()
            }
        }
    }
    $ini
}

###JDK 的檢測 查看 Java 路徑,一般來講,launcher 腳本會從 launcher.cfg 讀取Posix(Windows) 節的 JAVA_HOME 鍵值,若是沒有 JAVA_HOME 的變量就讀取環境變量的 JAVA_HOME,若是存在 JAVA_HOME,但實際路徑上並不存在,或者沒有存在 JAVA_HOME,那麼再從查找 java的路徑。而 JAVA_HOME 的設置能夠在有多個JDK的時候仍然正確的選擇 JDK.而不用帶來衝突。code

jdkenv=$(GetPrivateProfileString launcher.cfg Posix JAVA_HOME)
	javabin=`which java`
	if [ -f "$jdkenv/bin/java" ]; then
		javabin="$jdkenv/bin/java"
	fi

Get-JavaSE 函數是爲了支持從註冊表查詢 JDK 安裝。因此單獨的寫了一個函數。server

Function Get-JavaSE
{
    $jdk=$env:JAVA_HOME
    #This is regedit search java
    return $jdk
}

$JdkRawEnv=$IniAttr["Windows"]["JAVA_HOME"]

$JavaEnv="$env:JAVA_HOME"
IF($JdkRawEnv -eq $null)
{
    $JavaEnv=Get-JavaSE
}else{
    $JavaEnv=$JdkRawEnv
}

###進程的管理 先說 PowerShell,PowerShell 是面向對象的,咱們能夠輕鬆的得到進城對象。
咱們使用 Start-Process 啓動一個進程。在這個 cmdlet 中 咱們添加 -PassThru 就能夠獲得一個進程對象

$ProcessObj= Start-Process -FilePath "${JavaExe}" -PassThru -Argumentlist "${VMOptions} -jar ${PrefixDir}\${AppPackage} $Parameters"  -RedirectStandardOutput "${StdoutFile}" -RedirectStandardError "${StdErrorFile}" -WindowStyle Hidden

$ProcessObj 能夠拿到進程的 Id,進程的鏡像名,以及進程的資源佔用狀況。 固然進程對象在 Get-Process 也是可用的。 使用 Get-Process 得到一個進程,若是有進程 id 再好不過。若是不存在 id 對應的進程則會拋出異常

$Obj=Get-Process -Id $javaid

結束一個進程須要 Stop-Process 只須要輸入 -Id id 便可。 而對於 Linux,有文件系統 /proc,一樣能夠實現 PowerShell 的功能。判斷進程是否存在能夠檢測 /proc/id 是否存在,能夠檢查 /proc/id/status 查看資源佔用什麼的。
咱們在 launcher 腳本所在目錄(一般也是 jar 包所在目錄)當啓動 java 進程成功後,咱們將 pid 寫入到 launcher.lock.pid 在須要中止和重啓的時候使用launcher.lock.pid 存儲的 id 來操做便可。 在 PowerShell 中能夠用 Get-Content 讀取 pid。在 Linux 中能夠用 cat。

啓動
Windows

launcher -start

Linux

./launcher -start

重啓

launcher -restart

中止服務

launcher -restart

查看狀態:

launcher -status

幫助:

launcher -help

###最終 上述代碼已經託管到 GIT@OSC
項目:http://git.oschina.net/ipvb/ServiceLauncher 使用MIT協議,歡迎 Pull Request.

相關文章
相關標籤/搜索