JavaアプリをWindowsサービス化する方法

Javaで作成したサーバアプリをWindowsで常駐される必要があったので

Windowsサービス化させてみた。

 

環境は以下の通り。

Javaバージョン:1.7.0.15

・Windows Server 2008 R2 64bit

Eclipse Helios SR2

 

今回は、Tomcatでも使用されているApache Commons Daemon(移行、Daemo)を使用した。

今回試した手順は以下の通り。

 

1. ダウンロード

Daemonに関するライブラリだったりバイナリをダウンロードする。

ライブラリは、

http://commons.apache.org/proper/commons-daemon//download_daemon.cgi

バイナリは、

http://commons.apache.org/proper/commons-daemon//binaries.html

からそれぞれ最新版を入手する。

 

ライブラリは、commons-daemon-1.0.13.jarとバージョン付きのJarファイル

なので、これをプロジェクトのビルドパスに加えておく。

 

2. コーディング

Windowsサービス開始と停止時のエントリポイントとなるクラスを用意する。

このクラスは、org.apache.commons.daemon.Daemonインタフェースを

実装する必要がある。

サービス開始時と停止時にコールされるメソッドを用意する。

このメソッドは、public staticでなければならない。戻り値はどの型でも大丈夫だけど、評価できないのでvoidが妥当。

Linuxで使用する場合は、Daemonインタフェースで定義されているメソッドを

Overrideして処理を記述する必要があるけど、今回はWIndowsなので無視する。

 

サンプルは、以下の通り。

サンプルをビルドして実行可能JarとしてServiceLauncher.jarを作成する。

 

ServiceLauncher.java

package hoge;
  
import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.DaemonInitException;
 
/**
 * サービスを起動停止するクラス.
 *
 */
public class ServiceLauncher implements Daemon {
 
    // ランチャー
    private static ServiceLauncher launcher;
 
    // サービス
   private Service service;
 
   /**
    * コンストラクタ.
    */
    public Service() {
    }
 
   /** 
    * サービス実行.
    *
    */
    public void run()  {
       
        service = new Service();
            ・
            ・
            ・
        try {
            Thread t = new Thread(service );
            t.setDaemon(true);
            t.start();
            // サービスが停止するまで制御を戻したらダメ
            t.join();
        } catch (InterruptedException e) {
            ・
            ・
        }
    }
  
   /**
    * サービス停止処理
    */
    public void terminate() {
        if (service != null) {
            service .stop();
        }
    }
 
   /**
    * Windowsサービス開始時にcommons Daemonからコールされる
    *
    */
    public static void startService(String args) {
        launcher = new ServiceLauncher();
        launcher.run(); // サービス停止まで制御もどってこないはず
    }
 
   /**
    * Windowsサービス停止時にcommons Daemonからコールされる
    *
    * @param args
    */
    public static void stopService(String args) {
        if (launcher != null) {
            launcher.terminate();
        }
    }
 
   /**
    * Linuxで使用した場合commons Daemonからコールされる
    * 未実装
    */
    @Override
    public void destroy() {
        // Ingore
    }
 
   /**
    * Linuxで使用した場合commons Daemonからコールされる
    * 未実装
    */
    @Override
    public void init(DaemonContext arg0) throws DaemonInitException, Exception {
        // Ingore
    }
 
   /**
    * Linuxで使用した場合commons Daemonからコールされる
    * 未実装
    */
    @Override
    public void start() throws Exception {
        // Ingore
    }
 
   /**
    * Linuxで使用した場合commons Daemonからコールされる
    * 未実装
    */
    @Override
    public void stop() throws Exception {
        // Ingore
    }
 
   /**
    * メイン関数
    * Windowsサービス時には未使用
    * @param args
    */
    public static void main(String[] args) {
        launcher = new ServiceLauncher();
        launcher.run();
        return;
    }
}

 

Service.java

package hoge;
 
/**
 * サービスクラス.
 *
 */
public class Service implements Runnable {
    // 実行中フラグ
    private boolean isRunning = false;
 
   /**
    * コンストラクタ.
    */
    public Service () {
    }
  
   /**
    * サービスを停止する。
    *
    */
    public void stop() {
        isRunning = false;
    }
 
   /**
    * スケジューラを開始する。
    *
    */
    @Override
    public void run() {
        isRunning = true;
 
        while (_isRunning) {
            ・
            ・
            ・
        }
    }
}

 

3. インストール

できあがったJarファイルをWindowsサービスに登録する。

使用するのは、バイナリとしてダウンロードしたprunsvr.exeとprunmgr.exe。

64bit OSで使用する場合は、amd64/にあるファイルを使用する。

 

prunsvr.exeをServiceLauncher.exeにprunmgr.exeをServiceLauncherw.exeに

それぞれリネームする。

上記exeファイルと作成したJarファイルを同じフォルダに置く。

 

パラメータが多いのでインストール用バッチファイルを作成する。

 

Install.bat

set EXEC_DIR=%~dp0
echo %EXEC_DIR%
set INSTALL_PATH=%EXEC_DIR%ServiceLauncher.exe
set CLASSPATH=%EXEC_DIR%ServiceLauncher.jar;<commons-daemon-1.0.13.jarへのパス>
# JVMのパスを指定する。オプションでauto指定もできるがうまくいかない
set JVM_PATH="C:\Program Files\Java\jre7\bin\server\jvm.dll"  
 
 
ServiceLauncher //IS//ServiceLauncher--DisplayName="ServiceLauncher" --Install=%INSTALL_PATH% --Startup=auto --Jvm=%JVM_PATH% --StartMode=jvm --StopMode=jvm --Classpath=%CLASSPATH% --StartClass=hoge.ServiceLauncher --StartMethod=startService --StopClass=hoge.ServiceLauncher --StopMethod=stopService --LogPath=%EXEC_DIR% --LogLevel=DEBUG --StdOutput=auto --StdError=auto
pause  

 

アンインストール用のバッチファイルも用意する。

 

Uninstall.bat

ServiceLauncher //DS//ServiceLauncher 
  
pause  

 

詳細は、

http://commons.apache.org/proper/commons-daemon//procrun.html

http://wiki.apache.org/commons/Daemon

を参照

 

 

4. サービス起動

prunmgr.exeをリネームしたServiceLauncherw.exeからサービスの起動停止を行う。

当然、通常のWindowsサービス起動停止手順でも可能。

 

prunmgr.exeでは他にもパラメータの変更ができる。

prunmgr.exeは、ファイル名=サービス名として認識する。

ファイル名内の'w'は無視するのでサービス名+'w'にリネームしておくと

サービス名にリネームしたprunsvr.exeと共存できるようになる。

 

JavaでJSTからGMTに変換する方法

JST時刻に-9時間しないといけないのかな、面倒くさいなと思っていたら、SimpleDateFormatを使えばいけるみたい。

 

JSTで取得した時間をGMTに変更して"2013/02/20 09:30"的なフォーマットで出力する。

 

public class DateHelper {

public static String convertJSTtoGMT(Date jstTime) {

  SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

  formatter.setTimeZone(TimeZone.getTimeZone("GMT"));

  return formatter.format(jstTime).toUpperCase();

}

}

使い方は、

// デフォルトのTimeZoneがJSTとする。

Calendar calendar = Calendar.getInstance();

String gmtDate = DateHelper.convertJSTtoGMT(calendar.getTime());

System.out.println(gmtDate);

円安について

金融緩和とは本来金利を下げることであり、通貨の量を増やすことは極めて危険なのだ。一度増やした通貨をうまく吸収し、インフレを抑え得た中央銀行を私は知らない。だからこそ、円安のためには、さらなる量的緩和よりマイナス金利の方が良いと言っているのだ。

 藤巻健史 プロパガンダ 2013/02/15

http://www.fujimaki-japan.com/takeshi/

READING HACKS!

READING HACKS!読書ハック!―超アウトプット生産のための「読む」技術と習慣

READING HACKS!読書ハック!―超アウトプット生産のための「読む」技術と習慣

 

READING HACKSを読んで気になった点の抜粋

 

○ 読書投資基準=70:20:10モデル 【P.80

・既存ビジネス領域への書籍投資に70%、既存ビジネスをサポートしうる、あるいは新しいビジネスになりうる領域の参考領域に20%、そして全く未知の書籍に10%を策様に意識する。

・たかだか10%ですが、新しい出会いが演出されている。

 

○ ビジネス書を読む意味は、「教訓の獲得」にある 【P.95

・ビジネス書を読む最重要課題は、「他人の経験から紡ぎだされたスキーマ(概念や法則性)」を獲得すべきことだ。スキーマとは、「これまでの経験から獲得している知識や経験によって裏付けられた物事の法則性」のこと。

・獲得した他人の教訓を常に現場検証すること。そして、その結果を自分のビジネスの規則として作り変えること。他人の教訓を実践で試して、本当に使える教訓を取捨選択し、自分向けにアレンジしていく。

 

○ 速読のコツは「高速で何度も眼を通すこと」 【P.96

・速読術のコツは、「高速で何度も眼を通すこと」。速読術とは、キーワードをあらかじめ意識し、その隙間を高速で埋めていく技術。

・問題意識を持ち、ヒントを見つけてから、そこをフックに高速で何回も読む。たとえば、堀にいくつか杭を打ち込んで足場とし、そこからブワ―っと砂を埋めていく、そんな感じ。

 

○ ポストイットで本に杭を打つ 【P.99

・ポストイットを杭に見立てて、気になる箇所にフラグを立てておく。

 ①赤のポストイット・・「著者が強調したい箇所。教訓になりえる箇所。」

 ②青のポストイット・・「自分の課題に対応している箇所。ビジネスのヒント」

 ③黄のポストイット・・「文章表現として、大変参考になる箇所」

・後でじっくり読むとき、どういう意味でマークを残しておいたのかを再確認しつつ、じっくり読み進めることができる。

 

○ スピード・リーディングの基本技術と段階論 【P.103

・スキャニングは「検索読み」と呼ばれ、目標とする情報に狙いを絞って、本を検索していく方法。目次、序論、結論を予めじっくり読み、キーワードなど当たりをつけておく。

スキミングとは、簡単に言ってしまうと「飛ばし読み」で、本全体にざっと眼を通し、その本の内容を簡単に把握する方法。

 

○ 著者が書いているすべての本を読んでみる 【P.121

・ある学問分野を理解するには、その道の大家を決めて、読みやすいエッセイのようなものから徐々に読んでいくと、1つの思想、1つの体系がだんだんと見えてくる。

 

○ ノウハウ本は鵜呑みにしないで「実験」するもの 【P.128

・仕事にも、読書にも、勉強にも、このレシピは必要で、わたしはある目的を達成するための方法がしっかりと書かれている本、いわゆるノウハウ本は読むべきだと思っています。

・ノウハウ本は実験して、自分に合ったものだけを吸収する。方法は人によって向き不向きがある。

 

○ パッションを維持する読書 【P.130

・自らの背筋をピンと伸ばすために、あるいは改めて仕事に対するパッションを呼び覚ますために「座右の書」は有効にワークします。

 

○ 「思考のホーム・グラウンド」を作る 【P.144

・ある分野の本を専門的に読み、実践を積み重ねていくと、無意識に「思考のホーム・グラウンド」が形成される。

・困ったらホーム・グラウンドの考え方に立ち戻って、もう一度考え直し、共通点と差異点を考えることで、直面する問題が整理される。

 

○ 読書の7割を垂直型読書に投資する 【P.148

・月に一万円読書に投資するとして、7000円は専門読書にあてるとする。これだけ読めば、おおよそ所属部署におけるスペシャリストたる知識を得ることは可能であろう。この「職種専門的技能」こそ、最も読書でカバーでき領域である。

 

○ 自分だけの「教訓ノート」を作る 【P.149

・重要なのは、読書から得た教訓をアレンジして自分の仕事のフィットさせること。

・解るということは、知ることで得た知識を自分流に変換でき、その結果、態度変容にまで行き着くことである。自分向けに言葉や法則をアレンジして、自分の教訓として貯金していくことが、「思考のホーム・グラウンド」を形成するコツ。

 

○ ホーム・グラウンドの知をあらゆる場面に持ち込む 【P.152

・思考のホーム・グラウンドの知をあらゆる場面に適用してみると意外な発見がある。ビジネスにおける専門から違う領域をメタファーで捉え、そこに新しい価値を見出すことに「ビジネスの新しい萌芽」の方法論を垣間見ることができる。

 

○ アウェーからの学びが専門をより強くする 【P.154

・自分の専門外でも、そこから示唆を得ることはたくさんあります。そういった「意味ある偶然」を読書計画のなかにも忍ばせておくのは重要なことです。

・1つの専門性(思考のホーム・グラウンド)を体得したら、積極的に外に出て、大いに刺激や示唆を得て、自分のビジネスにポジティブ・フィードバックを引き起こすことが重要。読書で言えば、垂直型読書から水平型読書へ移行すべき段階。

 

○ ビジネス資料は、解剖しながら読む 【P.165

・今見ている資料が今後自分のアウトプットの参考になるか否かという視点でも見る。

・ビジネス資料は会議後に次の項目に意識して解剖しておくと便利。

 ①アウトプットの内容・構造のお手本として使えるか

 ②説得材料(データ)として使えるか

 ③アウトプットのデザイン見本帳として使えるか

・ビジネス資料は読むと同時に「解剖」し、自分のアウトプットに直結する素材として、集めておくと便利です。それはすなわち、現状のビジネスを回していくと同時に、未来への準備も行う構えをとるということ。

 

○ 残す資料は「最終企画書」と仕事を総括する「教訓ノート」 【P.171

・紙ベースで残す資料は、最終企画書、つまり、プレゼンテーションで使ったり、お客様に提出した資料のみ。

・最終企画書を見ながら、終わった案件を振り返り、自分の仕事を一度総括する習慣をつけること。

・終わったからではなく、終わった資料から、未来へつなげる経験知を紡ぎだすために、もう一度案件資料を読む。この読みは自分の経験に基づいたものですから、何かしら頭をよぎることがあるはずです。ですから、改めてストックしているファイルを読み直し、過去から教訓を利マインドさせる作業もお勧め。

 

RSSリーダーで自分のキーワードは必ず把握する 【P.175

・あらかじめキーワードをRSSリーダーに入れておくようにし、ウェブ上の情報を読みながら仮説を構想し、それを実際の調査で検証していく。

 

○ 先輩や部下に説明することを前提に資料を読む 【P.179

・会議の資料でも、自分が作成した資料でも何でも、その資料を必ず誰かに説明するという前提に常に立っておくこと。

・ビジネスで資料を読む時の構えは常に複眼的であることが大切なのです。1つは自分のアプトプットに活用できるパーツを探すこと。そしてもう1つは、直面しているビジネスをより深く理解するために、誰かに説明する前提で読むこと。

 

○ 超アウトプットのためのハニカム・データベース・システム 【P.186

・超アプトプットのためのデータベース

 ①アイデアファイル・・雑誌で好きなもの。美しいコレクション

 ②携帯電話メモ・・日々の思いつきネタ帳

 ③データファイル・・調査、統計データ

 ④ブログ・・時事情報、読書カード、読書感想

 ⑤教訓ノート・・これまでの教訓。他者の経験からの共感

 ⑥名作ファイル・・過去のベスト企画コレクション

 

○ ブレーン・ストーミング読書で壁を突破する 【P.192

・「意味ある偶然」を呼び寄せ、壁を乗り越える方法が、このブレーン・ストーミング読書です。

・全く未知の領域へ積極的にチャレンジし、そこでの学びを持ち帰るリバース・モードがリーディングにも必要なのです。

 

○ ブログを「個人データベース」として活用する 【P196

・ブログを単なる日記としてではなく、アウトプットのための情報データベースと捉えなおし、必要情報をネット上に公開し、さまざまな人の意見をも取り入れる編集装置として機能させれば、ビジネスマンとしてかなり有効な武器を手に入れることになるはず。

・ブログにおいて次の3つの要素に分けてデータを蓄積する。

 ①読書カード・・参考となる文献の文章、ページ数、著者名、書籍名を記載する

 ②日記・・その日の出来事を克明に記し、そこで考えた仮説を描く

 ③写真

 

○ 超アウトプットのためのデータベース準備を 【P.208

・アウトプッターいうのは、表層的な表現の下に潜む「ルール」や「ものごとの道理や本質」を見抜き、それを自分の表現の方法に転化させ、価値あるものを生み出していく人です。

・わたしたちは少しでも早くアイデアを形にするシステムを「個人」で持ち、来るべきチャンスに立ち向かう準備をしておくべき。

 

 

vimでjslintを使うためには

 

jslをインストール

 ※Red Hat系で確認

 

 jslソースコードを取得

 jslを展開してコンパイル

  # tar zxvf jsl-0.3.0-src.tar.gz

 # cd jsl-0.3.0/src

 # make -f Makefile.ref

 jslをPATHが通っている適当なところに配置

 # cp -p jsl /usr/loca/bin

 

jsl.vimをインストール

 jsl.vimを取得

 http://www.vim.org/scripts/script.php?script_id=2630

 からダウンロード

 jsl.vimを以下のディレクトリに配置

 # cp -p jsl.vim ~/.vim/compiler/ (該当するディレクトリがなければ作る)

 

vimの設定

 ~/.vimrcに以下の設定を追加

 " JavaScript Lint

 if !exists('b:current_compiler')

     compiler jsl

 endif

 autocmd QuickFixCmdPost make copen

 

vimからjslを使用する

 vimからコマンド:makeでOK

 

 

NodeとExpress3とSocket.IOでチャット

ようやく、Express3でチャットサンプルを動作させることができたのでメモる。

Express3では、Socket.ioまわりの扱いが変更になっているので注意。

 

動作環境

OS: CentOS6.3 64bit

Node.js: v0.8.12

Express: 3.0.0rc5

Socket.io: 0.9.10

 

Expressアプリを作成する。

テンプレートエンジンは、ejsを使用する。

# express -e chatdemoapp

 

Socket.IOをインストールする。

# cd chatdemoapp

# npm install socket.io

 

依存関係を解決する。

# npm install -d

 

app.jsを修正してサーバ側を実装する。

app.js

 var express = require('express')

  , routes = require('./routes')

  , user = require('./routes/user')

  , http = require('http')

  , path = require('path');

 

var app = express();

 

app.configure(function() {

  app.set('port', process.env.PORT || 3000);

  app.set('views', __dirname + '/views');

  app.set('view engine', 'ejs');

  app.use(express.favicon());

  app.use(express.logger('dev'));

  app.use(express.bodyParser());

  app.use(express.methodOverride());

  app.use(app.router);

  app.use(express.static(path.join(__dirname, 'public')));

});

 

app.configure('development', function(){

  app.use(express.errorHandler());

});

 

// index.ejsで変数portが参照できるように変更

// app.get('/', routes.index);

app.get('/', function(req, res) {

  res.render('index', { locals: { port: app.get('port') } });

});

 

app.get('/users', user.list);

 

// ここを変更

//http.createServer(app).listen(app.get('port'), function(){

//  console.log("Express server listening on port " + app.get('port'));

//});

var server = http.createServer(app).listen(app.get('port'), function(){

  console.log("Express server listening on port " + app.get('port'));

});

 

// ここからSocket.IO周りの実装を追加 =====>

// Express3では、Http.ServerインスタンスをSocket.IOにアタッチしないとだめみたい

// 参照:http://socket.io/#how-to-use

var io = require('socket.io').listen(server);

io.sockets.on('connection', function(socket) {

  console.log('connect');

  socket.on('message', function(msg) {

    socket.emit('message', msg);

    socket.broadcast.emit('message', msg);

  });

  socket.on('disconnect', function() {

    console.log('disconnect');

  });

});

// <===== ここまで

 

// ここ追加

console.log('Server running at http://localhost:' + app.get('port') + '/');

  

views/index.ejsを修正してクライント側を実装する。

サーバ側でSocket.IOをListenできてれば、クライアントは、/socket.io/socket.io.js

でSocket.IOを参照できるようになる。

 

views/index.ejs

<!DOCTYPE html>

<html>

  <head>

    <title>hoge</title>

        <script type="text/javascript" src="https://www.google.com/jsapi"></script>

        <script type="text/javascript">google.load("jquery", "1.4.4");</script>

        <script type="text/javascript" src="/socket.io/socket.io.js"></script>

        <script type="text/javascript">var port = <%= port %>;</script>

        <script type="text/javascript" src="/javascripts/client.js"></script>

    <link rel='stylesheet' type="text/css" href='/stylesheets/style.css' />

  </head>

  <body>

        <form action="" id="form">

          <input type="text" name="message" id="message" />

          <input type="submit" />

        </form>

        <hr />

        <dl id="list"></dl>

  </body>

</html>

 

クライアント用のJavaScriptファイルを新規作成する。

public./javascripts/client.js

 $(function() {

    var socket = io.connect(null, { port: port });

    socket.on('connect', function() {

        console.log('connect');

    });

    socket.on('message', function(msg) {

        var date = new Date();

        $('#list').prepend($('<dt>' + date + '</dt><dd>' + msg + '</dd>'));

    });

    socket.on('disconnect', function() {

        console.log('disconnect');

    });

 

    $('#form').submit(function() {

        var message = $('#message');

        socket.send(message.val());

        message.attr('value', '');

        return false;

    });

});

 

サーバを起動する。

# node app.js

 

クライアントから接続する。

ブラウザから、

にアクセスすればOK