まつざきの技術メモ

エンジニアの私的な技術メモです。


コメントする

Fukuoka Perl Workshop #23に参加してきました

僕はJava屋ですが、最近個人的に一番アツいプログラム言語はPerlです(マジで)

Perlは数ヶ月前から勉強を始めて、最近業務で使い始めましたばかりの「Perlゆとり世代」な中年なのですが、
Fukuoka Perl Workshopに初参加してきました。
(モダンPerlの定義とかPSGI/Plack以前のCGIの頃とか全くわかりません><)

ATNDはこちら
Fukuoka Perl Workshop #23

まずは、@dragon3さんのBacklogでPerlとPythonを使っているお話。
人気サービスの裏側のお話が聞けて面白かったです。

次に、@d_tsuruさんの開発環境とMojoliciousのお話。
Mojoliciousは名前だけ知ってたのですが、他のWAFとの比較とか含め調べて見ようかなと思いました。

そして、@myfinderさんのお話。
YAPC2012のトークで感銘を受け(”古典的でも地道にやる”というフレーズ)、WEB+DBの特集も読んでいたので、今回の勉強会をとても楽しみにしてました。

僕も、開発、ハードウェア、ミドルウェア、運用管理に関わっているエンジニアなので、
共感できるお話、参考になるお話ばかりでした。
広い視野でビジネスに関わる仕事が出来る会社って良いなぁと改めて思いました。

・ハードウェア
Memtestでエラーが無くても問題は出るそうで。やっぱりそうですよね。。
ECCメモリじゃないやつで32GBとか怖い><
バランサやスイッチのお話で、「ハードでやるべき事はきちんとハードで」大事ですね!
それと、見せて頂いたラックの棚、かなり欲しくなりましたw
弊社は自作サーバじゃないですが、あれはかなり使えそうです!!

・AWS
弊社もデータセンターとAWSを併用してます。今後海外展開もあるので参考になりました。

・分散データストア
やっぱりRiakをきっかけに、今年はErlangが来るんじゃないかなぁと思いました。勉強しようかな。

最後に、@myfinderさんの旅費を出して頂いたJPAさん、ヌーラボさん、ご参加の皆さんありがとうございました!

非常に有意義な時間を過ごせました。
また参加しますのでよろしくお願いしますm(_ _)m

広告


コメントする

nginxをリバースプロキシにしてGlassFishでクラスタリング

GlassFishクラスタ構成を作った時のメモです。

バージョンは、
OS: Scientific Linux 6.3 x86_64
nginx: 1.2.6
GlassFish: 3.1.2.2
Java: Java SE 7u11
いずれも今日時点の最新安定バージョンです。

アプリはJavaEE6で作ってます。
ステートフルなアプリなので、スティッキーセッションとセッションレプリケーションが必要です。

 

構成はこんな感じ。
DAS(ドメイン管理サーバ)がクラスタを管理します。
# 今回はGlassFishクラスタの話なので、「nginxとDASの冗長化は?」というツッコミは無しでお願いします:)

GlassFishのレプリケーション方式はインメモリレプリケーションです。
# 昔のGlassFishではHigh-Availability Database (HADB)という方式があったようですが、
# もう無くなったみたいですね。

レプリケーションについてはこのページがわかりやすいです。
Clustering in GlassFish Version 3.1

では、クラスタ構成の手順です。
このブログエントリーを参考にさせて頂きました。ありがとうございます!
Glassfish 3.1の自己増殖クラスタを試す

1. ノード準備
※この手順だけはDASではなく、各ノードで行います。

  • glassfishユーザ作成(GlassFishはglassfishユーザで実行する事にします)
  • GlassFishインストールディレクトリ作成
  • GlassFishインストールディレクトリのオーナーをglassfishに変更
  • Javaインストール
groupadd glassfish
useradd -g glassfish glassfish
mkdir /usr/local/glassfish3
chown glassfish:glassfish /usr/local/glassfish3
rpm -ivh /path/to/JDKのrpmファイル

2. クラスタ作成

asadmin create-cluster cluster1

※EC2環境で稼働させる場合、マルチキャストが使えないため以下のように明示的にノードを列挙してください。
詳しくはこのページを参照。
Discovering a Cluster When Multicast Transport Is Unavailable
@yoshioteradaさんに教えて頂きました。ありがとうございます!

※同一ノードにインスタンスを複数立ててクラスタを複数作る場合は、ポートが被らないように注意しましょう。

asadmin create-cluster --properties GMS_DISCOVERY_URI_LIST=tcp'\\:'//glassfish01,tcp'\\:'//glassfish02,tcp'\\:'//glassfish03:GMS_LISTENER_PORT=9090 cluster1

3. DASからノードへのssh接続設定

echo "AS_ADMIN_SSHPASSWORD=ノードのglassfishユーザのパスワード" > /tmp/password
asadmin --passwordfile=/tmp/password --interactive=false setup-ssh --sshuser glassfish --generatekey=true glassfish01
asadmin --passwordfile=/tmp/password --interactive=false setup-ssh --sshuser glassfish --generatekey=true glassfish02
asadmin --passwordfile=/tmp/password --interactive=false setup-ssh --sshuser glassfish --generatekey=true glassfish03
rm /tmp/password

4. DASからsshでノードのjavaコマンドが使えるか確認

ssh glassfish01 java -version
ssh glassfish02 java -version
ssh glassfish03 java -version

5. ノードにGlassFishをインストール

asadmin install-node --installdir /usr/local/glassfish3 --sshuser glassfish --sshkeyfile ~/.ssh/id_rsa glassfish01
asadmin install-node --installdir /usr/local/glassfish3 --sshuser glassfish --sshkeyfile ~/.ssh/id_rsa glassfish02
asadmin install-node --installdir /usr/local/glassfish3 --sshuser glassfish --sshkeyfile ~/.ssh/id_rsa glassfish03

6. DASにノードを追加

asadmin create-node-ssh --nodehost glassfish01 --sshuser glassfish --sshkeyfile ~/.ssh/id_rsa --installdir /usr/local/glassfish3 glassfish01
asadmin create-node-ssh --nodehost glassfish02 --sshuser glassfish --sshkeyfile ~/.ssh/id_rsa --installdir /usr/local/glassfish3 glassfish02
asadmin create-node-ssh --nodehost glassfish03 --sshuser glassfish --sshkeyfile ~/.ssh/id_rsa --installdir /usr/local/glassfish3 glassfish03

7. ノードにインスタンスを追加

asadmin create-instance --node glassfish01 --cluster cluster1 instance1
asadmin create-instance --node glassfish02 --cluster cluster1 instance2
asadmin create-instance --node glassfish03 --cluster cluster1 instance3

8. スティッキーセッションのためのプロパティ設定
この設定を行うと、Cookieの末尾にインスタンス識別子が付加され、
リクエストが振り分けられるインスタンスが固定されます。

asadmin create-system-properties --target instance1 INSTANCE=i1
asadmin create-system-properties --target instance2 INSTANCE=i2
asadmin create-system-properties --target instance3 INSTANCE=i3
asadmin create-jvm-options --target cluster1 -DjvmRoute=\${INSTANCE}

9. クラスタを起動
※OS起動時のクラスタの自動起動がわかりませんでした。ご存知の方教えて下さい><

asadmin start-cluster cluster1

GlassFishの設定はここまで。
次はnginxのインストールと設定です。

nginxは標準でスティッキーセッションの機能がありませんが、
モジュールを作ってくれている方がいらっしゃるので、ありがたく使わせて頂きます。

1. nginxインストール

ソースからインストールするので、ユーザを作ります。

groupadd nginx
useradd -g nginx nginx

依存ライブラリのインストール

yum install pcre-devel

ソースと追加モジュールのダウンロード

cd /usr/local/src
wget http://nginx.org/download/nginx-1.2.6.tar.gz
svn checkout http://nginx-sticky-module.googlecode.com/svn/trunk/ nginx-sticky-module-read-only

インストール
※各種パスとモジュールの指定は適宜読み替えてください。

tar xvf nginx-1.2.6.tar.gz
cd nginx-1.2.6
./configure \
  --prefix=/var/www \
  --user=nginx \
  --group=nginx \
  --sbin-path=/usr/local/sbin/nginx \
  --conf-path=/etc/nginx/nginx.conf \
  --error-log-path=/var/log/nginx/error.log \
  --http-log-path=/var/log/nginx/access.log \
  --pid-path=/var/run/nginx/nginx.pid \
  --lock-path=/var/lock/subsys/nginx.lock \
  --with-http_ssl_module \
  --with-http_realip_module \
  --with-http_gzip_static_module \
  --with-http_stub_status_module \
  --add-module=../nginx-sticky-module-read-only
make
make install

2. 設定ファイル修正
nginxのconfigureの時に–conf-pathで指定したパスに設定ファイルが出来ています。
以下、設定例です。
※スティッキーセッションに必要な設定は、upstreamのstickyだけです。

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log debug;

pid        /var/run/nginx/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" '
                      '$upstream_response_time';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    tcp_nopush      off;

    keepalive_timeout  65;

    gzip  off;

    server_tokens off;

    upstream cluster1 {
        sticky;
        server glassfish01:28080 max_fails=2 fail_timeout=10m;
        server glassfish02:28080 max_fails=2 fail_timeout=10m;
        server glassfish03:28080 max_fails=2 fail_timeout=10m;
    }

    server {
        listen       80;

        proxy_set_header Host               $host;
        proxy_set_header X-Real-IP          $remote_addr;
        proxy_set_header X-Forwarded-Host   $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;

        error_page  404              /404.html;
        error_page  500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        location / {
            root   /var/www/html;
            index  index.html index.htm;
        }

        location /favicon.ico {
            root   /var/www/html;
        }

        location /warファイルコンテキスト名/ {
            proxy_pass    http://cluster1;
        }
    }
}

以上で環境は出来ました。
動作確認してみます。

1. 確認用ソース
以下をsession.jspという名前で保存して、warファイルを作成します。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ page import="java.net.InetAddress" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
<%@ page import="javax.servlet.http.Cookie" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css">
<!--
body
{
	font-family: 'MS ゴシック';
	font-size: 9pt;
}

table
{
	border-collapse: collapse;
	border-left: 1px;
	border-right: 1px;
	border-top: 1px;
	border-bottom: 1px;
	border-style: solid;
	border-color: #000000;
}

table.border
{
	border-left: 0px;
	border-right: 0px;
	border-top: medium solid;
	border-bottom: 0px;
	border-color: #000000;
}

td
{
	border-left: 0px;
	border-right: 1px;
	border-top: 0px;
	border-bottom: 1px;
	border-style: solid;
	border-color: #000000;
	margin: 2px;
	padding: 2px;
}

td.title-left
{
	background-color: #a3bacd;
	font-weight: bold;
	text-align: left;
}

td.title-center
{
	background-color: #a3bacd;
	font-weight: bold;
	text-align: center;
}
-->
</style>
</head>
<body>
<h1><font color="red">Session Info</font></h1>
<table align="center" border="0" width="800">
	<tr>
		<td class="title-left" width="120">Server</td>
		<td><%= InetAddress.getLocalHost().getHostName() %></td>
	</tr>
	<tr>
		<td class="title-left" width="120">Session ID</td>
		<td><%= session.getId() %></td>
	</tr>
	<tr>
		<td class="title-left" width="120">Created on</td>
		<td>
<%
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
			out.print(sdf.format(new Date(session.getCreationTime())));
%>
		</td>
	</tr>
	<tr>
		<td class="title-left" width="120">Request Count</td>
		<td>
<%
			if (session.getAttribute("count") == null) {
				session.setAttribute("count", new Integer(0));
			}
			int count = ((Integer)session.getAttribute("count")).intValue() + 1;
			session.setAttribute("count", new Integer(count));
			out.print(count);
%>
		</td>
	</tr>
</table>

<br/>
<table border="0" width="100%" class="border">
	<tr>
		<td width="80%"></td>
	</tr>
</table>

<h1><font color="red">Cookie Info</font></h1>
<table align="center" border="0" width="800">
	<tr>
		<td class="title-center">Name</td>
		<td class="title-center">Value</td>
		<td class="title-center">Domain</td>
		<td class="title-center">Path</td>
		<td class="title-center">MaxAge</td>
		<td class="title-center">Comment</td>
		<td class="title-center">Secure</td>
		<td class="title-center">Version</td>
	</tr>
<%
	Cookie[] cookies=request.getCookies();
	if (cookies != null) {
		for (int i=0; i<cookies.length; i++) {
			Cookie cookie = cookies[i];
			out.print("<tr>"  + "\r\n");
			out.print("<td>"  + cookie.getName()    + "</td>" + "\r\n");
			out.print("<td>"  + cookie.getValue()   + "</td>" + "\r\n");
			out.print("<td>"  + cookie.getDomain()  + "</td>" + "\r\n");
			out.print("<td>"  + cookie.getPath()    + "</td>" + "\r\n");
			out.print("<td>"  + cookie.getMaxAge()  + "</td>" + "\r\n");
			out.print("<td>"  + cookie.getComment() + "</td>" + "\r\n");
			out.print("<td>"  + cookie.getSecure()  + "</td>" + "\r\n");
			out.print("<td>"  + cookie.getVersion() + "</td>" + "\r\n");
			out.print("</tr>" + "\r\n");
		}
	}
%>
</table>

</body>
</html>

2. デプロイ
Jenkinsを使ってビルドからデプロイまで自動化するのが良いと思います。

asadmin --host=glassfish00 --port=4848 --passwordfile=/path/to/.glassfishpasswd deploy --target=cluster1 --contextroot=warファイルコンテキスト名 --force=true --property java-web-start-enabled=false:preserveAppScopedResources=true /path/to/warファイル

3. ブラウザで確認
http://nginxホスト名/warファイルコンテキスト名/session.jsp
を表示して、リクエストの度に「Request Count」の値がカウントアップされる事、
「JSESSIONID」の値が変わらない事を確認します。

4. GlassFishのWeb管理コンソールから、テスト中のセッションを処理しているノードを停止します。

5. もう一度ブラウザで確認
http://nginxホスト名/warファイルコンテキスト名/session.jsp
を再度表示して、「Request Count」の値が初期化されずにカウントアップされる事、
「JSESSIONID」の値はインスタンス識別子の部分だけ変わる事を確認します。

以上で完了です。

最後に
nginx + GlassFishの組み合わせは情報が少なかったです。
昔からApacheを使うのが当たり前ではありますが、(マニュアルにも載ってるし)
個人的には、nginxの方が設定がわかりやすくパフォーマンスも優れていると思います。


コメントする

JenkinsバージョンアップしたらWindowsでスレーブが起動しなくなった

Jenkinsを1.480.2にバージョンアップしたら、Windowsでスレーブが起動しなくなったので
対応した内容をメモ。
(Linuxのスレーブはバージョンアップしても問題なく起動しました。もうWindowsいやだ・・・。)

  • Jenkins管理画面で、スレーブの起動方法をJNLPに変更
    (バージョンアップ前はWindowsサービスでJenkinsスレーブを起動してました)
  • スレーブを自動ログインにする
  • スレーブにバッチファイルを作成して、スタートアップでスレーブを起動

 

バッチファイルの内容

set JENKINS_HOME=Z:\jenkins_home
set JENKINS_LIB=Z:\jenkins_lib

java -jar %JENKINS_HOME%\slave.jar -jnlpUrl Jenkinsスレーブを起動するためのURL(slave-agent.jnlp) -jnlpCredentials ユーザ名:パスワード -classpath %JENKINS_LIB%\commons-codec-1.7.jar

commons-codec-1.7.jarは事前にダウンロードして、任意のディレクトリに配置しておいてください。
配置場所はどこでもOKです。

 

Windowsで自動ログインにするのが嫌は場合は、バッチに以下の1行を追記すればロックがかかるのでOKかと思います。

rundll32.exe user32.dll,LockWorkStation


コメントする

2012年の振り返りと2013年の目標

まずは、2012年を振り返るにあたり今年の初めに書いたエントリーを読み返してみました。
2011年の振り返りと2012年の目標

去年と比べると勉強会・イベントはかなり沢山参加できました。
参加と主催を合わせると34回参加なので、月平均は約3回。
今年もかなり忙しい1年だったけど、これだけの回数参加できたのは家族や同僚のおかげだと思います。
ありがとうございますm(_ _)m

また、毎日1〜2時間勉強する事を目標に上げてましたが、
月〜土はどんなに遅く帰っても、ほぼ毎日1〜2時間勉強する事が出来ました。
来年もこれは継続していきます。

それと、2012年に勉強する事としては、以下をあげてました。

  • Ruby
  • Python
  • Scala
  • Asakusa Framework
  • MongoDB
  • 英語

・Ruby
業務でChefとFluentdを使い始めたので書く機会はそれなりにあったけど、Railsはやらなかったです。
ちょっと勉強してみたけど、あんまり馴染めなくて挫折・・・。

・Python
やってない・・・。
言語としてはPerlに興味が湧いたので、Perl勉強しました。

・Scala
やってない・・・。
業務で使う言語とフレームワークを今使ってるJava + Seasar2からScala + Play2.0に移行するつもりだったけど、
Java + JavaEE6に移行しました。

・Asakusa Framework
業務での使いどころはあるのだけど(弊社が作ってるのは基幹業務システムなので)、
他に優先するべき事が多いので、業務で使うのはまだまだ先になります。

・MongoDB
元々はFluentdのデータストアとして考えてたんだけど、
MySQLを使うことにしました。
ちょっとした用途で使う分にはとても便利だと思います。

・英語
仕事で海外の人とメールでやりとりする事が何度かあり、今後も増えそうな気配なので引き続き勉強します。

 

2013年に勉強したい事は、

  • データベース
  • Linuxカーネル
  • Hadoop周辺の技術
  • ソフトウェアテスト
  • 数学

データベースについては、
@kisさんがされてたみたいに自分で機能を実装してみる。
Postgres, MySQLのソースレベルで理解を深める。
業務でMySQLも使い始めたので、運用の知識を深める。
の3点。

・Linuxカーネルについて
まずは「詳解 Linuxカーネル 第3版」を読む事から始めます。

・Hadoop周辺の技術について
Asakusa FrameworkとかHiveとかImpalaとか勉強したいです。
業務で役立つ日が必ず来るので。

・ソフトウェアテストについて
恥ずかしながら、この辺の知識が圧倒的に不足してます・・・。

・数学について
自分の知識レベルではこれ以上プログラマとしての成長が難しいと判断したからです。
恥ずかしながら、まずは高校数学レベルの勉強から始めます・・・。

 

以下、今年参加した勉強会・イベント、読んだ本などのまとめです。

 

参加した勉強会・イベント(順不同)
29回 (抜けがある気がする・・・)

  • RBC イケテルRails3.1勉強会
  • 【RBC Casual Talks】 恋する Jenkins
  • FukPUG Workshop vol.0
  • FukPUG Meet-up vol.2
  • 第0回 福岡Debian勉強会
  • 第1回 福岡Debian勉強会
  • アマゾン ウェブ サービス/Asakusa Frameworkセミナーin福岡
  • 勉強会Play1.1
  • javaでデザインパターンを書いて手を動かそう#3
  • javaでデザインパターンを書いて手を動かそう#4
  • javaでデザインパターンを書いて手を動かそう#6
  • Hadoop関連技術勉強会@福岡
  • Webサーバ勉強会
  • Future Sync vol.2
  • JavaQne(じゃばきゅん) Fukuoka 2012
  • JavaOne SF 2012報告会
  • hktstudy #01 ~恋せよ乙女~
  • MySQL Casual Talks @gumi福岡
  • MySQL Casual Talks in Fukuoka vol.2
  • Oracle MySQL Tech Tour – Fukuoka
  • 第3弾 チューニンガソン
  • 第4弾 チューニンガソン
  • 第1回福岡Jenkins勉強会
  • G*ワークショップ in 福岡
  • ISUCON2
  • 福岡Ruby会議01
  • 福岡でOpscode Chef ハンズオン(無料半日版)
  • オープンソースカンファレンス2012 Fukuoka
  • Hadoopとかその他もろもろ勉強会in福岡0回

 

主催した勉強会
5回

  • 第0回 インフラ勉強会@福岡
  • 第1回 インフラ勉強会@福岡
  • 第2回 インフラ勉強会@福岡
  • 第3回 インフラ勉強会@福岡
  • 第4回 インフラ勉強会@福岡 Fluentdハンズオン

 

LTした回数
9回

 

 

 

 

 

 

 

 

 

書いたブログエントリー
16回 (このエントリーを含む)

 

参加したAdvent Calender
1つ

  • JavaEE Advent Calendar 2012

 

読んだ本(積読本は省く)
23冊

  • MongoDB and Python
  • Hadoop: The Definitive Guide 3rd Edition
  • Gitによるバージョン管理
  • 継続的デリバリー
  • Jenkins
  • Play framework Cookbook
  • 詳解 MySQL
  • デコンパイリングJava ―逆解析技術とコードの難読化
  • Beginning Java EE 6
  • アルゴリズムを学ぼう
  • Mobageを支える技術 ~ソーシャルゲームの舞台裏~
  • J2EEパターン 第2版
  • 徹底解剖「G1GC」 アルゴリズム編
  • Jenkinsではじめるビルド職人入門
  • RDBMS解剖学
  • プログラミングGROOVY
  • はじめて学ぶソフトウェアのテスト技法
  • Apache Maven 3クックブック
  • 実践テスト駆動開発
  • 初めてのPerl 第6版
  • リーダブルコード
  • Amazon Web Services クラウドデザインパターン 設計ガイド
  • JUnit実践入門


コメントする

NetBeansでJavaEE6開発する時にHotDeploy可能なプロジェクトタイプ

これはJavaEE Advent Calendar 2012の11日目のエントリです。

昨日は@mike_neckさんの「CDIについて書こうとしたら、JSRを読み始めて、結果JSR330を実装してみようとしてみた件について」でした。

弊社ではNetBeans&GlassFishを使っています。
今回のエントリーでは、JavaEE6初心者な僕が最初に困った点について書こうと思います。
これから始める方の、何かのお役に立てれば!

# 業務で使う標準フレームワークを最近JavaEE6に切り替えたばかりです。
# ちなみに、その前はSeasar2(S2Dao + Teeda)&Eclipse&Tomcatでした。

NetBeansでJavaEE6なWebアプリを開発する場合、プロジェクトのタイプは以下の4つが選べます。

  1. NetBeans独自形式プロジェクト + warパッケージング
  2. NetBeans独自形式プロジェクト + earパッケージング
  3. Maven形式プロジェクト + warパッケージング
  4. Maven形式プロジェクト + earパッケージング

 

JavaEE6になってから、ejbもwarファイルにパッケージングが出来るようになったのは
皆さん既にご存じかと思います。

 

各プロジェクト作成ウィザードのスクリーンショットはこちら。
 

1. NetBeans独自形式プロジェクト + warパッケージング
スクリーンショット 2012-12-11 15.29.26

2. NetBeans独自形式プロジェクト + earパッケージング
スクリーンショット 2012-12-11 15.29.41

3. Maven形式プロジェクト + warパッケージング
スクリーンショット 2012-12-11 15.33.45

4. Maven形式プロジェクト + earパッケージング
スクリーンショット 2012-12-11 15.34.01

弊社ではMaven形式プロジェクト + earパッケージングで開発を始めました。
理由としては、開発するサービスが非常に大きいからです。
クラス数が多いとAPサーバの起動に時間が掛かるし、メモリも消費するので、
ある程度の機能単位でejb(jar)に分けて、導入するお客様の環境で必要な分だけパッケージングしようと考えました。

こんな感じです。

  • A社様向け → 機能1 + 機能2 + 機能3
  • B社様向け → 機能2
  • C社様向け → 機能1 + 機能3

ですが、earでパッケージングしてしまうと、開発が始まって非常に困った問題が発覚。
ソースの変更を行った場合、変更を反映するために再ビルド&GlassFish再起動が必要という状況(ToT)
これでは開発の効率が非常に悪くなってしまいます。

そこで、warファイルにパッケージングしてみたところ、ソースを変更して保存したタイミングで、
アプリのHotDeploy(と呼んでいいのかな?)が行われました。

プロジェクトタイプごとのHotDeployの挙動をまとめるとこんな感じです。

  1. NetBeans独自形式プロジェクト + warパッケージング → HotDeploy可能
  2. NetBeans独自形式プロジェクト + earパッケージング → HotDeploy不可能
  3. Maven形式プロジェクト + warパッケージング → HotDeploy可能
  4. Maven形式プロジェクト + earパッケージング → HotDeploy不可能

 

GlassFishの管理コンソールでアプリケーションがデプロイされたパスを確認して、
そのディレクトリの中身を見てみると、
HotDeploy可能な場合はclassファイルがフラットな状態で配置されていて、
不可能なプロジェクトタイプではjarとかwarにパッケージングされて配置されてました。

1. NetBeans独自形式プロジェクト + warパッケージング
スクリーンショット 2012-12-11 22.29.53

2. NetBeans独自形式プロジェクト + earパッケージング
スクリーンショット 2012-12-11 22.30.09

3. Maven形式プロジェクト + warパッケージング
スクリーンショット 2012-12-11 22.31.18

4. Maven形式プロジェクト + earパッケージング
スクリーンショット 2012-12-11 22.30.34
 

NetBeans独自形式プロジェクトのビルドツールはAntみたいですね。
せっかく新しく始めるんだし、もうAntは使いたくないので、
Maven形式プロジェクト + warパッケージングでやる事にしました。
(今時はGradleなのでしょうか?)
サーバのメモリ消費問題は、warにパッケージングする時に不要な分は除外する方法で対応するつもりです。

規模が大きい開発の場合、みなさんどうされているのでしょうか。。。(ERPとか)
「earパッケージングでもHotDeploy出来るよ!」というツッコミ等あれば是非お願いしますm(_ _)m

明日(2012/12/12)は@kikutaro_さんです。
よろしくお願いします!!


コメントする

福岡Ruby会議01でLTしてきました

福岡Ruby会議01が開催されたので、参加してきました。

主催者の@Spring_MTさん、会場をご提供いただいたGuildCafe Costaさん、
参加された皆さん、ありがとうございました!
おもしろいトークとLTが満載で、とても楽しませていただきました。

Rubyも楽しい!!

僕は業務でRubyを使い始める時にやった事についてLTさせてもらいました。

会社で新しい事を始めようとすると、上司や同僚の反対など色々あると思いますが、
僕は以下のようにやっています。

  1. まずは自分で使ってみて検証する
  2. 社内布教活動をする
  3. 小さなツールでもいいので導入実績を作る

 

導入するとメリットがあるプロダクト・ツールがあれば話がスムーズだと思います。
ChefとFluentdはどの会社でも導入メリットがあると思うのでお薦めです!


2件のコメント

CDIで複数データソースの切り替え

将来的にはJavaEEの仕様にマルチテナント機能が組み込まれますが、
僕は今のところ、複数データソースの切り替えが出来れば十分なので、
CDIを使ってやってみました。
これで1つのJavaEE6アプリで複数のデータベースへの接続を切り替える事が可能です。

では早速、実際のソースを見てみましょう。

 

まずはプロデューサ・メソッドから。
EntityManagerがDIされるフィードを複数持ち、プロデューサ・メソッドで何れかをreturnします。
今回の例ではシステム日付の秒で分岐しましたが、
実際はURLパラメータやCookieに保持した値などで分岐する事になると思います。
また、RequestScopedアノテーションを付けて、リクエスト毎に呼ばれるようにします。

public class EntityManagerFactory {

    @PersistenceContext(unitName = "pu01")
    private EntityManager em01;
    @PersistenceContext(unitName = "pu02")
    private EntityManager em02;

    @RequestScoped
    @Produces
    @EntityManagerQualifier
    public EntityManager getEntityManager() {
        // システム日付が偶数秒ならem01に接続する。
        DateTime dt = new DateTime();
        int sec = Integer.parseInt(dt.toString("s"));
        if (sec % 2 == 0) {
            return em01;
        } else {
            return em02;
        }
    }
}

次にDIでEntityManagerを受け取る側
DIで受け取ったEntityManagerを使うだけです。
今回の例ではコントローラに書いてます。

@Model
public class SampleController {

    @Inject
    @EntityManagerQualifier
    private EntityManager em;

    public String getName() {
        Query query = em.createNamedQuery("findAll", Sample.class);
        return ((Sample) query.getSingleResult()).getName();
    }
}

EntityManagerQualifierアノテーションはQualifier(限定子)です。
インジェクションの対象を限定(紐付け?)させています。

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface EntityManagerQualifier {
}

ソースはGitHubにアップしてます。
READMEに環境構築の手順メモも書いてます。
サンプルソース

以上、久々のJavaネタでした!