2020年12月23日水曜日

New Remote Normal (Management 3.0 Japan Conference 2020)


 Management 3.0 Advent Calendar
23日の記事になります。

2020年はもうコロナでアレでしたね。私の場合はプライベートでもわりと大きめなコトがあり、なんだかあっという間でした。幸いに仕事もさせていただけているし、イワナ釣りにハマったり、それなりに充実してたかなと思います。



2020/9/12にManagement 3.0 Japan Conference 2020が行われました。
あらためて、スポンサーの皆様、登壇いただいた皆様、スタッフ、そして参加いただいた皆様本当にありがとうございました。
カンファレンス のキーノートの一つは、Lisette Sutherlandさんの講演、"Navigating the New Remote Normal" でした。Lisetteさんは『リモートワーク(原題: Work Together Anywhere)』という本の著者で、このコロナ渦にタイムリーなお話でした。
  • Define What is Normal & Expected
  • Make Communication Easy
  • Explore New Ways of Being "Present"
リモートワークを成功させるためのアイデアをこの3つの軸で展開していました。
1つ目ののDefine What is Normal & Expectedは、
  • チームアグリーメント
  • 何をもって完了とするか?
  • 労働時間では無く成果での評価
など、たとえリモートじゃなくてもちゃんとしておいた方がいいという内容でした。

2つ目のMake Communication Easyは、リモートでのコミュニケーションを簡単に・円滑にするためのアイデアなんですが、その中には
  • ミーティングの時間・回数を減らし質を上げる
  • 非同期コミュニケーションをうまく使う
など、これもまたリモートじゃなくても有効なものがありました。

3つ目のExplore New Ways of Being "Present"は、チームが同じ場所にいない状態で、どう存在感をお互い感じる・感じさせるか。『そこにいる感』『一緒にいる感』をどう作り出すかという話でした。カメラをonにして顔を出した方がいいとか、バーチャルオフィスツールの紹介などですね。日本のコミュニティではDiscordが急速に普及しました。イベントではもう必須と言えるかもしれません。ただテキスト、音声で話すだけならSlackでもできるのに、Discordが流行ったのは『どの部屋に誰がいる』という状態をDiscord上でできることが理由かなと思います。

こんな感じで、まさに今皆が必要としているような内容でした。
リモートであるなしにかかわらず取り組んだ方がいい内容も結構ありました。そしてそれらは、リモートになるとその必要性・有効性が際立つというのは私の体感としてあります。例えば、伝えたい内容や対象によって何を使うか(Slackなのか通話なのか..)を決めておくとかです。私も遅まきながら、現場でチームアグリーメントをまとめたりしました。

急激な変化は誰もが混乱します。特に日本人、日本の組織は急に何かを変えるのが苦手だと思います。しかし、コロナによって問答無用に変えさせられました。私の関わっている現場でもギリギリまでフルリモートでの業務遂行に踏み切れず、リモートで業務が成り立つのか?という感じでした。それでも緊急事態宣言によって強制的にリモート生活に切り替えられました。私が関わっている現場では、主に営業マンが使う目的でリモートから社内ネットワークにアクセスする仕組みがあったので、急なリモートへのシフトがなんとかなりましたが、なかったらどうなっていたか... 私はManagement 3.0のコミュニティなどリモートでの活動の経験がで得たものをフル活用して、チームのリモートワークはどうにかしました。アナログカンバンなどは全て封印されましたが別のツールでどうにかして、リモートになってもカンバンも日時のミーティングもすべて何もやめずに推めることができています。『集まらなくなった』という大きな変化以外は何も変えずいつも通りにしたかった。たまたまできた、という部分もありラッキーだったと思います。

コロナによって、『変われない』『決められない』という弱点が露呈した(している)なと思います。自己組織化、自己マネジメントができるチーム、組織が強さを際立たせる世の中になってきているのかなと思います。

年単位で放置していたこのブログをこんなに短期間に3つも書くなんて自分でもびっくりですが、続くかな。



2020年12月22日火曜日

決め方を決める

 Management 3.0 Advent Calendar 2020 22日の記事になります。 思い立って突然投稿してみました。


前回Scrum Fest Sapporoの話を書きました。Scrum Fest Sapporoで私が話した内容のメインはDelegation Pokerを使った話でした。Delegation Pokerは、ある『意思決定領域』に対して、マネジメント側とチーム側のどちらが決めるか、そのレベルを決めるというものです。





そして、2020/12/16にTACO(Tokyo Agile Community)のイベントで、ステファンと寳田さんでManegement 3.0の新しいモジュール、"Teams" の話、そしてTeam Decision Matrix のワークショップを行いました。
Team Decision Matrixはチーム内での意思決定の仕方を決めるというものです。



いずれも『決め方を決める』という物です。
何かを決める前に決め方を決めなくちゃならんのかと思うと、やたら面倒な話に思えますが、どう決めるかってのは「モノによる」んですよね。実際会社でモノを買ったりするにも金額とかで決済の条件が決められていたりします。
皆さん実際どうしてますか? 例えばフレームワークやツールの選定とか、プロセスとか。チームが納得していない決定って効果的ですか? まさに「モノによる」じゃないかと思います。
  • マネージャーが決める
  • チームが決める
  • 有識者が決める
  • チーム全員で決める
  • 多数決で決める
  • なんでもいい
それぞれモノによって、その時のチームの状況によってどれを選択すべきか変わってきそうです。「これについてはこう決める」ということを決めておくのは、その決定に対する納得感のためには効果的かと思います。チームの状態は変化してゆくので、定期的に見直したりする必要はありそうです。私もDelegation Pokerを2回やりました。
なによりも、その対象についてメンバーがどう認識しているかをお互いに知ることができます。カードを出して、そのカードを選択した理由を表明することによって、対象をどう捉えているかの違いもわかります。そしてそこからその対象をどう捉えるべきかという議論ができます。こういうプロセスを経て、決め方が決まるのがとてもチームにとっていい影響、効果があります。


「納得感のない決定は効果的か?」というのは、Lean Change Managementの研修から引用した物です。次回そういう話を書こうかな。

2020年12月12日土曜日

SCRUM FEST SAPPORO 2020にManagement 3.0ネタで参加しました

Management 3.0 Advent Calendar 2020 12日の記事になります。

11月に私とStefanで、SCRUM FEST SAPPORO 2020に参加しました。

ホントは4月に予定されていたのですが、コロナがアレで11月に延期、そしてオンライン開催となりました。いろいろ大変だっと思いますが開催していただいてありがとうございます。とても素晴らしいイベントでした。
ホントは札幌行ってイベント参加して、そのままイトウ釣りチャレンジしちゃおうかなとか考えてました。行きたかったな。

私たちのセッションを簡単にまとめると、私が現場でManagement 3.0のプラクティス(Celebration Grid, Delegation Poker, Moving Motivators)を実践してみた話をして、ステファンがMoving Motivators, Delegation Pokerのワークショップを行いました。私自身とても楽しめました。
参加いただいた皆様、本当にありがとうございました。

スクフェス札幌で話したネタの中で、私が本当に実感しているのは「人は、自分が扱われる通りの行動をするものです。」ってやつです。



これはManagement 3.0の研修資料の1ページなんですが、本当にその通りだなと感じています。
私が現場でDelegation Pokerを使ってみて、再確認したとういうか、改めて気づかされたものです。
これは『上司が部下をどう扱うか』だけではなく、部下が上司にどう接するか、または職位の上下に関係なく言えることだと思います。「この上司は話してもわかってくれないだろう」という態度だったら、その上司はきっとなにも考えてくれない。こんな感じで噛み合わない時って、多分お互いに「きっとこうだろう」と決めつけちゃってるかもしれません。


Management 3.0には様々な課題に対するアプローチのヒントがあります。もっと知られるといいな。私もそのために微力ながらボチボチ活動してゆきます。




2018年3月2日金曜日

5つ目の価値

ブログの存在をもう忘れてしまっていたが、また書こうと思う。
またすぐに忘れてしまいそうだけど。

スコット.W.アンブラーの「アジャイルモデリング」の第2章には、「アジャイルモデリングの価値」として

  • コミュニケーション
  • 簡潔さ
  • フィードバック
  • 勇気
  • 謙虚さ

の5つが挙げられている。
4つめまではXPの価値と同じ。
この本の出た頃(2003年)はXPの価値は勇気までの4つだった。スコット.W.アンブラーは5つめの「謙虚さ(Humility)」を足した。
そして、2004年にXP本の第2版では5つ目に「尊重(Respect)」が加えられている。

5つめの価値が追加された事、これは必然なんだろうと思う。
どんなに技術力があろうがお金があろうが、この謙虚さや尊重というモノが無ければ恐らく何事も上手くいかない。
忘れがちだが、この5番目を失う時全ての価値が失われる気がする。

自戒を込めて。

2016年6月17日金曜日

MyBatisでLocalDate, LocalDateTime使う

4月に MyBatis-TypeHandlers-JSR310 1.0.0 がリリースされてました。知らんかった。
myBatisのエンティティにjava8のDate and Time APIを使うためのタイプハンドラです。
せっかく書いたけど、オレオレハンドラとはお別れです。


  • pom.xml
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-typehandlers-jsr310</artifactId>
  <version>1.0.0</version>
</dependency>


  • mybatis-config.xml
<typeHandlers>
  <typeHandler handler="org.apache.ibatis.type.InstantTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.LocalDateTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.LocalDateTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.LocalTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.OffsetDateTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.OffsetTimeTypeHandler" />
  <typeHandler handler="org.apache.ibatis.type.ZonedDateTimeTypeHandler" />
</typeHandlers>


これでエンティティクラスにLocalDate, LocalDateTime等が使えると。

2013年12月5日木曜日

Genba is not dead または Genba will never die

この記事はDevLOVE Advent Calendar 2013「現場」 の27日目です。
kimura_m_29さんからバトンを受け取りました。

自己紹介

はじめまして。羽飼康(はがいやすし)と申します。
ソフトウェアの開発を生業としております。主に受託開発のお仕事をさせていただいています。
名古屋アジャイル勉強会、DevLove名古屋のスタッフをさせていただいております。

現場

現場ってなんでしょうねって思って、私が最初にイメージした『現場』はこんな感じです。

  • 工作機械の製造現場
  • 工作機械を使ってアルミなどを削り出している製造現場
  • 物流の現場
  • 土木工事の施工現場
これらは私がお仕事で関わることのあった『現場』です。
特に『土木工事現場』は私にとってとても大切な思いのある『現場』だったりします。
こうやって見てみると建設業だったら工事現場、製造業であれば製造の現場というように、それぞれの組織・業界において、「取引の対象となるモノ(コト)」が産み出されている場所を『現場』と呼んでいる事が多いように思います。我々の業界でも『現場』というと、やはりソフトウェア開発を行っている場をそう呼びますね。
とはいうものの、我々がシステム化対象業務の実態を知るために「現場を見る」とか「現場の声を聞く」とか言って、実際の業務が行われている有様を見させてもらったり、話を聞かせてもらったりします。この場合必ずしも「生産」の場では無い。
営業活動だったり、いわゆる事務であったり、システム運用業務の場合もありました。
実はすべてのコトが行われている場は『現場』なんですね。それぞれの現場があって、ある現場で閉じているモノなど一つも無くて、すべて現場と現場の関わりで成り立っている。システム開発に関わっている我々はそれを知っているはずなのです。
例えば『モノを買う』という行為は、

  • 生産するための材料を入手するというコト
  • 取引先に発注するというコト
  • 仕分けを起票するというコト
  • ウォッチすべき原価に変化が起きるというイベント
という具合に、それぞれの現場・役割においてする事も意味も異なったりします。それでも目指すべき大きなゴールは同じであるはずです。

『現場意識』を強く持っているのはおそらく『生産の現場』です。これはその現場の中の人も外の人もそう思っていることが多いと思います。
こういう現場を指す『現場』という言葉は、水戸黄門の印籠のように使われることがあります。
『現場』または『現実』はこうだっ!と言い放つことによって思考停止に陥るアレです。悲しいことに我々の業界でこの印籠が頻繁に使われちゃってないかなぁって思います。
いろんな現場が相互作用して成り立ってるって事を外から見てよく知ってるはずの我々がそれやっちゃってるんですね。それで軋轢が生まれたり壁が出来たりする。
『現場』 of 『現場』でそれやっちゃったらね。
あまりにもったいないので、そういうのはやめたらいいかな。

そんな愚痴は置いておいて、自分関わる現場は前向きに前進させたい。
今年は特にそんな気持ちで仕事に取り組んだつもりです。
勉強会などででもいい刺激をたくさん受けることが出来ました。現場の外での学びを現場に持ち込んだり。私に関わっていただいた皆さんに心より感謝申し上げます。
わりと勢いよくチャレンジしたように思います。うまくいったり苦労したりでした。幸いすばらしいメンバーの力でどうにかなってます。
いい歳して落ち着き無くて楽しかったな。わがままにつきあってくれたメンバーありがとう。
そしていつもの事ながら力不足を痛感したりしました。歩みの遅いおっさんはいつまでも学びがあって退屈しません。
私の現場を外から見てた人は、良いとこだけ持って行ってください。

なんか年末なのでこういう感じになっちゃいました。
来年はもうちょっといろいろうまくやれるかな。

バトン

明日は@shin_semiyaさんです。ガンギマリだそうです。
きっとアッパー系でしょう。


2013年5月25日土曜日

Agile Japan 2013 サテライト<名古屋>

昨日Agile Japan 2013が開催されました。 
名古屋のサテライト会場は『モデリングxアジャイル』テーマで、名古屋アジャイル勉強会が主催で開催。
午後のワークショップは私が担当しました。
YOU&Iさんをはじめとする名古屋アジャイル勉強会のスタッフのみなさん、ドメイン駆動設計読書会@名古屋(DDDNagoya) 主催のRKTMさんと参加者のみなさんのサポートによりなんとかできました。
心より感謝申し上げます。


今回は「アジャイルにモデリング」ということで、モデルの書き方ではなく、どういう『姿勢』でモデリングするかという事が主題。
特に、

  • モデルを使ってコミュニケーション
  • インクリメンタルにモデリングする

という2点を意識した内容でいきました。

今回のワークショップの準備で、スコット・W・アンブラーの『アジャイルモデリング』をそれなりにちゃんと読んでたりして、モデリングという行為について良く考えるいい機会になったと思う。
普段の仕事でも、もう少し無駄を省けるかなという気がする。

”モデリング”と言うと、表記法などモデルそのもの書き方が話題の中心となることが多い。
まあそれはとても普通の事なんだけど。
『アジャイルモデリング』は、アジャイルにモデリングに取り組む『姿勢』が語られている本。タイトルに「モデリング」という言葉がはいっていて、これだけ記法などが書かれていない本は他にないんじゃないかな。
今回のワークショップで、参加者の皆さんにその”姿勢”が伝わっていたらとても嬉しいです。


  • 複数人でモデリングするという事
  • やりすぎてわけわかんなくなる前に実装してみるという事

が、結構イイ感じである事は皆さん感じていただけたのかなと思う。

普段の仕事でも、ホワイトボードに簡単にモデルを書いて「こうじゃね?」という感じでディスカッションする事は時折あって、生産性の高いコミュニケーションになっていると思う。
ドメイン駆動設計読書会@名古屋(DDDNagoya) でも、参加者みんなでわいわいと意見を出し合いながらモデリングしたりして、これがまた楽しかったり(私はなかなか参加できていないが…)。
また、Qcon Tokyo 2013でも、原田騎郎さんの『DDD を Scrum で廻す。あるいは Scrum を DDD で廻す。』というセッションがあって、その中で複数人でホワイトボードでやるのが良いと言われていた。
そうする事によって、メンバーのモデリングスキルも上がってゆく。
もっと積極的にこのやり方を使っていきたいと思う。




2013年1月15日火曜日

ExtJS 覚書 #2 validation

Ext.data.Modelを使ったクライアントサイドバリデーション

データモデルにvalidationの定義を含めることができる。
Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: [
        {name: 'name',     type: 'string'},
        {name: 'age',      type: 'int'},
        {name: 'phone',    type: 'string'},
        {name: 'gender',   type: 'string'},
        {name: 'username', type: 'string'},
        {name: 'alive',    type: 'boolean', defaultValue: true}
    ],
    validations: [
        {type: 'presence',  field: 'age'},
        {type: 'length',    field: 'name',     min: 2},
        {type: 'inclusion', field: 'gender',   list: ['Male', 'Female']},
        {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
        {type: 'format',    field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
    ]
});

バリデートはExt.data.Modelのvalidate()で行う。
結果は、Ext.data.Errorsが返される。

var errors = instance.validate();

サーバサイドバリデーション

サーバサイドのバリデーションの結果をExt.data.Model.validate()と同じ形で得るためには、データモ
デルのproxyにreaderを正しく設定する必要がある。
 Ext.define('Employee', {
    extend : 'Ext.data.Model',
    idProperty : 'id',
    fields : [
        { name : 'id', type : 'string' },
        { name : 'firstName', type : 'string' },
        { name : 'lastName', type : 'string' },
        { name : 'gender', type : 'string' }
    ],
    proxy : {
        url : "/コンテキストルート/employeeService",
        type : "rest",
        reader : {
            type : 'json',
            messageProperty : 'errors'
        }
    }
});

このデータモデルのsave()メソッドに対して、サーバ側バリデーションを返す場合、以下のようなJSONデータをサーバは返さなければならない。
{
    'success' : false,
    'errors' : { 'id' : 'Error Message1', 'firstName' : 'Error Message 2', 'lastName' : 'Error Message3' }
}

readerのmessagePropertyの値と、JSONデータのエラーのプロパティを一致させること。
バリデーション結果は、モデルのload(), save(), destroy()メソッドのsuccess, failureコールバックの引数 operation (ext.data.Operation) から得られる。
var emp = Ext.ModelManager.getModel('Employee');
emp.set('123', 'Fidel', 'Castro', 'Male');
emp.save({
    waitMsg : 'Saving...',
    success : function(operation){ },
    failure : function(operation){
        var errors = operation.getError();
    }
});

validationの結果をformに適用する
formのmarkInvalid()を使ってvalidationの結果をformに適用する事が出来る。
var errors = instance.validate();
form.markInvalid(errors);

jax-rs(jersey)覚書 #4 自前のシリアライザー : 日付フォーマットを指定する

jax-rsはオリコウさんで、pojoがそのままjsonになって飛んでいきます。
ところが、java.uti.Dateのフィールドがイケてない形で行ってしまいました。
その結果、Ext JSのグリッドやformのdate型のフィールドに表示されず。
jsonにシリアライズするときにフォーマットの指定できないかしらと調べてみた。

まず、org.codehaus.jackson.JsonGenerator を継承したシリアライザーを作る。
serializeというabstractのメソッドを実装してあげればよい。
Date型を yyyy/MM/DD にするならこんな感じ。

import java.io.IOException;
import java.text.SimpleDateFormat;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;

public class MyDateSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator gen,
   SerializerProvider provider) throws IOException,
   JsonProcessingException {

        SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
        String formattedDate = formatter.format(value);

        gen.writeString(formattedDate);
  
    }
}

そして、jsonに変身するクラスの、作ったシリアライザーを適用したいフィールドを @JsonSerialize アノテーションで修飾する
public class Person {
 
    private String firstName;
    private String lastName
    
    @JsonSerialize(using = MyDateSerializer.class)
    private Date birthday;
    

こんな感じでした。

ExtJS覚書 #1 Ext.data.ModelでREST


ExtJSでは、Ext.data.Modelを拡張したデータモデルをを使って、正しくRESTでJSONデータをやり取りする事できる。
データのライフサイクルまでそれなりに管理してくれていて便利ではあるが、何をしてくれるかを正しく理解する必要がある。
ここでは、モデルとフォーム(Ext.form.Panel)を使って一意のデータをCRUDする方法を説明する。

データモデル(Ext.data.Model)とCRUD

id, 姓, 名, 性別 という要素を持つEmployeeというデータをCRUDするRESTのクライアントの定義は以下のようになる。
idPropertyはこのデータモデルのIDとなるプロパティを指定する。
proxyのtypeに"rest"を指定することにより、Ext.data.proxy.Restが使われる。正しくRESTの型にはまる。

Ext.define('Employee', {
    extend : 'Ext.data.Model',
    idProperty : 'id',
    fields : [ 
        { name : 'id', type : 'string' },
        { name : 'firstName', type : 'string' },
        { name : 'lastName', type : 'string' },
        { name : 'gender', type : 'string' }
    ],
    proxy : {
        url : "/コンテキストルート/employeeService",
        type : "rest"
    }
});

データをロードする(loadメソッド)

このデータモデルを使ってデータをロードするコードは以下の通り。
var emp = Ext.ModelManager.getModel('Employee');
emp.load('123', {
        waitMsg : 'Loading...',
        success : function(record, operation){ },
        failure : function(record, operation){ }
        });

これで、 http://ホスト/コンテキストルート/employeeService/123 にGETリクエストが送られる。
success, failure関数の引数recordには、得られたデータ(Employeeのインスタンス)が入る。

データモデルをformにセットする

form(Ext.form.Basic)のloadRecord()を使って、データモデルをformにセットする事が出来る。
この時データモデルの内容で、formの要素は書きかえられる。
データモデルのfieldと、formの要素の名前(と型)が一致していればOK。
var emp = Ext.ModelManager.getModel('Employee');
emp.load('123', {
        waitMsg : 'Loading...',
        success : function(record, operation){
            empForm.getForm().loadRecord(record);
        },
        failure : function(record, operation){ }
        });

データを新規作成する(saveメソッド)

Ext.ModelManager.create()を使ってデータモデルを新規作成する。
save()メソッドでJSONデータをPOSTする。
var emp = Ext.ModelManager.create('Employee',{id : '123', firstName : 'Fidel', lastName : 'Castro', genger: 'male'});
emp.save({
        waitMsg : 'Saving...',
        success : function(record, operation){ },
        failure : function(record, operation){ }
        });

これで、 http://ホストコンテキストルート/employeeService に、JSONデータがPOSTされる。
success,failure時の関数の引数 record はにデータモデルのインスタンス、operationには、ext.data.Operationのインスタンスがセットされる。
ここで、setId()メソッドは使ってはならない。この件については『データを更新する』のセクションで説明する。
成功時、operationのconfig、"action"には"create"が入っている。

formのデータを使って新規作成する

フォームのデータを使って作る場合はExt.ModelManager.create()の第2引数をform.getValues()にすればよい。
formの要素と、データモデルのフィールドの名前と型は合わせておく。
var form = this.up('form').getForm();
var emp = Ext.ModelManager.create('Employee', empForm.getForm().getValues(false));
emp.save({
        waitMsg : 'Saving...',
        success : function(record, operation){
            empForm.getForm().loadRecord(record);
        },
        failure : function(record, operation){ }
        });

データを更新する(saveメソッド)

load()されたModelでsave()メソッドを使うとJSONデータがPUTされる。
この時のURLはproxyで設定したURLに"/id文字列"が付いたものになる。
上記の例で、idPropertyに設定したフィールド(デフォルトは"id")の値が"123"であった場合、
http://ホスト/コンテキストルート/employeeService/123
となる。
新規作成もsave()メソッドを使う、save()メソッドによってPOST(create)されるのかPUT(update)されるのかは、phantomというプロパティがtrueかどうかで決まる。trueならPOSTとなる。
このphantomというプロパティは、Modelのidプロパティ(デフォルトはid)に値が入っているかどうかで決まる。
model.setId(値);を実行するとphantomがfalseにセットされるようになっている。
どうもExt.data.Modelは、

  • idは一つ
  • idは新規作成時に付与される

というポリシーで作られている。
複合キーであったり、id(コード等)をユーザが決める場合はこの事を意識してプログラミングする必要がある。


formからデータモデルのインスタンスを取り出してPUTする

form(Ext.form.Basic)のgetRecord()を使って、formからModelのインスタンスを得る事が出来る。form.loadRecord()によってセットされているModelが得られる。
フォームにModelがロードされていない状態(loadRecord()されていないform)では、getRecord()はundefinedを返す。
ただし、ここで得られたrecordは、フォーム上での編集が反映されていない(編集前のデータが残っているということ)。
form(Ext.form.Basic)のupdateRecord()で、現在のフォームのデータを反映する事が出来る。

var emp = form.getRecord();    /* フォームよりデータモデルのインスタンスを得る */
form.updateRecord(emp);        /* 現在のフォーム上のデータでモデルの内容を更新する */
emp.save({
        waitMsg : 'Saving...',
        success : function(record, operation){ },
        failure : function(record, operation){ }
        });

成功時、operationのconfig、"action"には"update"が入っている。

データを削除する(destroy()メソッド) 

削除するコードは以下の通り。
var emp = Ext.ModelManager.getModel('Employee');
emp.set('123', 'Fidel', 'Castro', 'Male');
emp.destroy({
        waitMsg : 'Deleting...',
        success : function(operation){ },
        failure : function(operation){ }
        });

これで、 http://ホスト/コンテキストルート/employeeService/123 にDELETEリクエストが送られる。
saveと同様に、JSONデータが送られる。
123の部分には、データモデル定義のidPropertyに指定したフィールドの値がセットされる。

jax-rs(jersey)覚書 #3 bean validation


これはjax-rsというわけではないが、セットで使うことが多いと思う。

JSR303 bean validationで、文字列の長さ、数値の大きさ、日付、nullなどの値チェックはほとんど可能。
使い方は非常に簡単で、validationしたいクラスのフィールドにアノテーションを設定すればよい。
javax.validation.constraintsには以下のアノテーションが定義されている。

  • @AssertFalse,@AssertTrue
  • @DecimalMax,@DecimalMin
  • @Max,@Min
  • @Digits
  • @Future,@Past
  • @Null,@NotNull
  • @Pattern
  • @Size

文字列の長さをチェックするには以下のようにする。
public class Employee {
    @Size(max=3, message="3桁までですよ!")
    private String empId;
    private String firstName;
    private String LastName;
    private String gender;

validationは、インターフェース javax.validation.Validator を使う。
SpringにDIさせるにはbean定義ファイルに以下の設定を行う。

;

実際にvalidationする例は以下の通り

@Service
public class EmployeeService {
    @Autowired
    private Validator validator;
    
    private void validateEmployee(Employee emp) {
        Set<Constraintviolation<Employee>> violations = validator.validate(emp);
    
        for (ConstraintViolation<employee> violation : violations) {
             System.out.print(violation.getPropertyPath().toString());
             System.out.print(" : ");
             System.out.println(violation.getMessage());
        }
    }

javax.validation.Validator.validate()で、javax.validation.ConstraintViolationのSetが得られる。
ConstraintViolation.getPropertyPath().toString()で、フィールド名が得られる。
ConstraintViolation.getMessage()で、アノテーションで設定したメッセージが得られる。

メッセージをプロパティファイルから得ることもできる(そうすべき)。
デフォルトのプロパティファイル名はValidationMessages.properties 。
public class Employee {
    @Size(max=3, message="{emp.id.size}")
    private String empId;

jax-rs(jersey)覚書 #2 基本編


サービスのパスの設定

@Pathアノテーション

クラス、メソッドに指定して、RESTサービスのパス(URL)を決める。
@Path("employee")
public class EmpService{
    
    @GET
    @Path("service1")
    public Response service1() {
この場合サービスのURLは
http://ホスト/コンテキストルート/employee/service1
となる。

可変パスの扱い
@Pathと@PathParam

RESTでは”一意なURL”というのがが基本的な考え方としてある。URLのパスにIDを含める形になる(”サービス/{id} ”というような形)。
jax-rsではパスの値が可変なURLを扱うことができる。
@Path("employee")
public class EmpService{
    
    @GET
    @Path("{id}")
    public Response service1(@PathParam String id) {
これで、
http://ホスト/コンテキストルート/employee/123
にアクセスした場合、 @PathParamアノテーションで修飾した引数 id に"123"が入ってくる。

複合キーならどうするか?

複合キーを"-"等で繋いだ場合、は以下のような形になる。
http://ホスト/コンテキストルート/employee/123-111 の場合

@Path("employee")
public class EmpService{
    
    @GET
    @Path("{id1}-{id2}")
    public Response service1(@PathParam String id1, @PathParam Strind id2) {


HTTPメソッドの指定

@GET, @PUT, @POST, @DELETE, @HEADアノテーションを使って、どのHTTPメソッドで受けるかを指定する。
同一パスでもHTTPメソッドによって処理を切り分けることができる。
@Path("employee")
public class EmpService{
    
    @GET
    @Path("{id}")
    public Response get(@PathParam String id) {
        
    }
    
    @PUT
    @Path("{id}")
    public Response update(@PathParam String id) {
        
    }
    
    @POST
    @Path("{id}")
    public Response insert(@PathParam String id) {
        
    }
    
    @DELETE
    @Path("{id}")
    public Response delete(@PathParam String id) {
        
    }
}
この場合、同じURL
http://ホスト/コンテキストルート/employee/123
を、HTTPメソッドを分けて使うことができる。正しいRESTの形はこのように実現できる。
実装されていないメソッドでアクセスがあった場合、405 Method Not Allowed がクライアントに返される。

POST,PUTされる様々なContent-TypeのデータをJavaオブジェクトへ変換する

@Consumesアノテーションで受け取るデータのContent-Typeを指定することができる。
たとえば以下のJSONデータをPOSTされて、Employeeというクラス(Value Object)にマッピングする場合は以下のようになる。

{ "empId" : "123", "firstName" : "Oscar" , "lastName" : "Jarjays", "gender" : "female" }

@Path("employee")
public class EmpService{

 @POST
 @Consumes({ MediaType.APPLICATION_JSON })
 public Response post(Employee employee) {

public class Employee {

    private String empId;
    private String firstName;
    private String LastName;
    private String gender;
    
    public String getEmpId() {
        return empId;
    }
    public void setEmpId(String empId) {
        this.empId = empId;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return LastName;
    }
    public void setLastName(String lastName) {
        LastName = lastName;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }

}
JSON以外のContent-Typeにも対応している。

  • application/json : MediaType.APPLICATION_JSON
  • application/xml : MediaType.APPLICATION_XML
  • application/x-www-form-urlencoded : MediaType.APPLICATION_FORM_URLENCODED
  • application/octet-stream : MediaType.APPLICATION_OCTET_STREAM
  • multipart/form-data : MediaType.MULTIPART_FORM_DATA
  • text/plain : MediaType.TEXT_PLAIN
  • etc..

Javaオブジェクトを様々なContent-Typeに変換してレスポンスを返す


@Producesアノテーションでレスポンスとして返すContent-Typeを指定する。
javax.ws.rs.core.Responseを使って、実際に変換を行ってレスポンスを返す。
EmployeeをJSONにして返す場合は以下のようになる。
@Path("myRestService")
public class MyRestService {
    
    @GET
    @Path("{id}")
    @Produces({ MediaType.APPLICATION_JSON })
    public Response get(@PathParam(name = "id") String id) {
        
        Employee emp = getEmp(id);
        return Response.ok(emp, MediaType.APPLICATION_JSON).build();
    }

Response.ok()ではHTTPステータスコード200を返す。

OK以外のレスポンスを返す
javax.ws.rs.core.Response.status() を使う。


  • status(int status)
  • status(Response.Status status)
  • status(Response.StatusType status)


  • Response.status(404);
  • Response.status(500);
  • Response.status(Status.FORBIDDEN);
  • Response.status(Status.INTERNAL_SERVER_ERROR);


クエリパラメータを扱う

http://ホスト/コンテキストルート/myRestService?param1=value1&param2=value2
という形で、パラメータを指定した場合は、引数を@QueryParamアノテーションで修飾することによって受け取ることができる。
@Path("myRestService")
public class MyRestService {
    
    @GET
    public Response get(@QueryParam(value="param1") param1, @QueryParam(value="param2" param2)) {

この方法だと、パラメータ値一つ一つをバラバラに受け取ることになる。
クラスにマッピングする方法もある。

まず、パラメータを受け取るクラスのフィールドに@QueryParamアノテーションをつける。
public class Employee {

    @QueryParam("id")
    String id;
    
    @QueryParam("firstName")
    String firstName;

    @QueryParam("lastName");
    String lastName;

サービスのメソッドの引数に@Contextアノテーションをつけたcom.sun.jersey.api.core.ResourceContextを指定する。
ResourceContext.getResource()にパラメータを格納するクラスを指定して、インスタンスを得ることができる。
これは@QueryParamだけでなく、@PathParam等にも使える。

@Path("myRestService")
public class MyRestService {
    
    @GET
    public Response get(@Context ResourceContext rc)) {
        
        Employee emp = rc.getResource(Employee.class);
このやり方の問題は、引数がResourceContextになってしまって、欲しいパラメータはResourceContextから得ることになってUnit Testがやりにくくなってしまう。
まとまった数のパラメータをPOJOで受け取りたいのであれば、POSTを使ってPOJOで受け取る方が良さそう。

jax-rs(jersey)覚書 #1 pom.xmlとweb.xml


jax-rs(jersey)あたりのネタはすでに色々落ちてるのですが、せっかくなので書いておくことにします。

mavenのdependency


  • group id: com.sun.jersey
  • artifact id:  jersey-core, jersey-server, jersey-json, jersey-grizzly2, jersey-spring
  • version: 1.16 (2013/01/07時点)

mavenでjersey-springの1.16を指定するとspring3.0のモノが落ちてくる。Springは3.1を使うのでexcludeしている。


    com.sun.jersey
    jersey-core
    1.16


    com.sun.jersey
    jersey-server
    1.16


    com.sun.jersey
    jersey-json
    1.16


    com.sun.jersey
    jersey-grizzly2
    1.16


    com.sun.jersey.contribs
    jersey-spring
    1.16
    
        
            org.springframework
            spring
        
        
            org.springframework
            spring-core
        
        
            org.springframework
            spring-web
        
        
            org.springframework
            spring-beans
        
        
            org.springframework
            spring-context
        
    



servletの設定(web.xml)

init-paramでcom.sun.jersey.api.json.POJOMappingFeatureをtrueに設定しないと、JSON(XML)とPOJOとのマッピングがされないので注意。
com.sun.jersey.config.property.packagesで、RESTサービスになるクラスのパッケージを指定する。


    Jersey REST Service
    com.sun.jersey.spi.spring.container.servlet.SpringServlet
    
        com.sun.jersey.api.json.POJOMappingFeature
        true
    
    
        com.sun.jersey.config.property.resorceConfigClass
        com.sun.jersey.api.core.PackagesResourceConfig
    
    
        com.sun.jersey.config.property.packages
        jp.onestepbeyond.ckndemo0.service.rest
    
    1

    

    Jersey REST Service
    /rest/*  


2012年12月22日土曜日

Ext JS4でコンボボックスのローディングマスクが止まらない

ExtJSのコンボボックスで、ローディングマスクが出っぱなしで選択できないという現象が出ちゃって困ってました。
よくある県のコンボボックスを選んだら市のコンボボックスの内容が変わるみたいなアレです。
この2番目のコンボボックスで問題は起きました。
色々頑張ったけど解決せず。
そしてExtJSのフォーラムでそれらしいのを発見。
ビンゴでした。

オチとしては ExtJS4.1を使いなさいと。
そもそもなんで4.0.7を使ったんだろ…

Ext JS Community Forums 4.x / Ext: Q&A / Problem with a never ending loading mask

Ext JS + jax-rs (jerseyでちょっとハマり)

Ext JS + jax-rsってのをやってます。

いいですね。コレ。
やりたいことはだいたい出来ます。いままでExt JSをやらなかったことをちょっと後悔。
jax-rsはjersey使ってます。

Spring + myBatis + jersey ( + struts2) という構成。


そんなJerseyで軽くハマりまして。
いつもの様にまずはググって出てきたサイトを参考にしながら作ってみたんだけど、JSONをPOJOにマッピングするのってが動かない。
色々調べたら、サーブレットのinit-paramに、↓↓こんなの↓↓


 com.sun.jersey.api.json.POJOMappingFeature
 true

が要ると。
Jerseyのドキュメントにモロに書いてありますね。
ちゃんと読みましょう。


Jerseyの黄色いジャージって、ランス・アームストロングのマイヨジョーヌのイメージなんだろうなって思うのですが、剥奪されちゃってアレですね。
でもグレッグ・レモンもツール連勝のアメリカ人レジェンドなのよん。

2012年11月17日土曜日

もろもろの認証をLDAPにする with OpenDJ

Apacheの基本認証(Subversion用)とredmineとjenkinsの認証をLDAPにする。
他にも増えそうだし、バラで管理するのは面倒なので。
LDAPサーバはOpenDJを選択。
以前OpenAM(OpenSSO)をいじったことがあって、あれはOpenDJ(OpenDS)がセットなので何となく慣れがあるような気がしたが、気がしただけだった。

1. OpenDJのインストール

  • OpenDJをgetしてunzip
# wget http://download.forgerock.org/downloads/opendj/2.5.0-Xpress1/OpenDJ-2.5.0-Xpress1.zip
# unzip OpenDJ-2.5.0-Xpress1.zip
  • 出来たディレクトリに移動してセットアップ
# ./setup --cli (← CUIでのセットアップはこのオプション)
  • 初期ルートユーザー DN
  • パスワード
  • 自己署名証明書の生成に使用される完全修飾ホスト名または IP アドレス
  • ポート(デフォルト:389)
  • 管理コネクタのポート(デフォルト:4444)
  • ベースDN
  • SSLを有効にするかどうか
  • LDAPSのポート
  • Start TLSを有効にするかどうか
  • 証明書サーバーのオプション:
を答えて出来上がり。
  • OS起動時に起動されるように設定
# /openDJをインストールしたディレクトリ/bin/create-rc-script --outputfile /etc/init.d/opendj
# chkconfig opendj on

2. LDAPにユーザ登録

  • WindowsにOpenDJをインストール(展開しただけ)して、コントロールパネルを使って登録。
  • LDAP検索用ユーザも作る
  • jenkinsの管理者ユーザも作る

3. Apacheの基本認証(Subversion用)

  • こんな感じ。
AuthType Basic
AuthName "なんか名前"
AuthBasicProvider ldap
AuthzLDAPAuthoritative off  #ldapがダメでもほかの認証に行かない
AuthLDAPURL ldap://hostname/ou=people,dc=foo,dc=bar?uid
AuthLDAPBindDN "cn=LDAP,ou=people,dc=foo,dc=bar"
AuthLDAPBindPassword "yourpassword"
Require valid-user

4. redmineのLDAP認証設定

  • 管理ユーザでログインして、設定 > LDAP認証 で "新しい認証方式”を登録。設定例は↓↓↓こんな感じ。
名称:ONE STEP BEYOND
ホスト:hostname
ポート:389
アカウント:cn=LDAP,ou=people,dc=foo,dc=bar
パスワード:*********
検索範囲:ou=people,dc=foo,dc=bar
LDAPフィルタ:
ログイン:uid
名前:givenName
苗字:sn
メールアドレス:mail
  • LDAP認証したいユーザの”認証方式”をLDAP認証で作ったものに変更。

5. jenkinsのLDAP認証設定

  • 管理ユーザでログインする。
  •  jenkinsの管理 > システムの設定 で アクセス制御-ユーザ情報のラジオボタンで"LDAP"を選択。
  • ”高度な設定”ボタンを押して、LDAPの情報を登録。
サーバー:hostname
root DN:dc=foo,dc=bar
未指定を許可(チェックボックス):チェックしない
User search base:oc=people
User search filter:uid={0}
Group search base:
管理者のDN:cn=LDAP検索ユーザ,ou=people,dc=foo,dc=bar
管理者のパスワード:*********
  • jenkinsはユーザごとに認証方式を選択できないので、設定を登録したらログアウトしないで別のブラウザでログイン可能かどうか確認する。

6.pam

sshでしか入らないので、OSの認証ってsuぐらいしか使わない。ということはほぼ使わない。
というわけで今回はやめ。

2012年11月10日土曜日

redmine code reviewプラグインのインストール

code review プラグインを入れたので、とりあえず書いておく。


redmine2.0.4にbacklogs0.9.26が入っている状態からインストール
まず、redmine_code_review0.6.0はredmine2.1が要るので入りません。0.5.0を入れます。
  • redmine_code_review-0.5.0をget
# wget https://bitbucket.org/haru_iida/redmine_code_review/downloads/redmine_code_review-0.5.0.zip
  • pluginsディレクトリに展開
  • bundle install
# bundle install --without development test postgresql sqlite
You cannot specify the same gem twice with different version requirements. You specified: simplecov (~> 0.6) and simplecov (>= 0)
と言われる。
redmine_backlogsのGemfileには "~> 0.6"が指定されているが、redmine_code_reviewの方はされていない。
redmine_code_review/Gemfileも
gem "simplecov", "~>0.6"
に書き換えてやりなおし。
  • マイグレート
# RAILS_ENV=production rake db:migrate_plugins 
  • オーナーをapacheに
# chown -R apache:apache redmineのインストールディレクトリ

なんかするたびにchownするのも忘れそうなので、apacheにsu出来るようにしようかな。
そもそもapacheとかmysqlの設定が/etcあたりにあったりするLinuxのデフォルト設定がいまだに違和感のあるSunOS/Solaris育ち。でもLinuxに慣れた人に触ってもらう事を考えるとあまりいじらない方がいいのかなと…

インストール作業はこれで終了。 

2012年11月9日金曜日

redmineとjenkinsの仕込み #4(SSL化編)


#3の続き。
redmineとjenkinsのSSL化


18. mod_sslのインストール

  • rootになる
  • mod_sslをインストール
#yum -y install mod_ssl

19. オレオレ証明書を作る 

  • 証明書の有効期限を長くする
/etc/pki/tls/certs/Makefileに "-days 365" ってのが3カ所あるので書き換える
  • 証明書を作る 
# make server.crt
umask 77 ; \
        /usr/bin/openssl genrsa -aes128 2048 > server.key
Generating RSA private key, 2048 bit long modulus
................+++
...................+++
e is 65537 (0x10001)
Enter pass phrase:
Verifying - Enter pass phrase:
umask 77 ; \
        /usr/bin/openssl req -utf8 -new -key server.key -x509 -days 3650 -out server.crt -set_serial 0
Enter pass phrase for server.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:Aichi
Locality Name (eg, city) [Default City]:Nagoya
Organization Name (eg, company) [Default Company Ltd]:ONE STEP BEYOND
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:ホスト名
Email Address []:メールアドレス
  • apache起動時にパスワードを要求されないようにサーバー用秘密鍵からパスワード削除
# openssl rsa -in server.key -out server.key

20. apacheの設定 

  • ssl用のDocumentRootを作る
/var/www/secure としてみた
# mkdir /var/www/secure
  • redmineのシンボリックリンクこっちに張る
# ln -s /usr/local/redmine-2.0.4/public /var/www/secure/redmine
  • 元のシンボリックリンクを消す 
# rm /var/www/html/redmine
  • 設定ファイル /etc/httpd/conf.d/ssl.conf はサンプルをベースに、以下の3カ所を編集
DocumentRoot "/var/www/secure/html"
SSLCertificateFile /etc/pki/tls/certs/server.crt
SSLCertificateKeyFile /etc/pki/tls/certs/server.key
  • jenkinsの設定をSSLの方に引っ越す 
/etc/httpd/conf.d/ajp.confの中身を/etc/httpd/conf.d/ssl.confに引っ越し
/etc/httpd/conf.d/ajp.confを削除
  • オーナーをapacheに
# chown -R apache:apache /var/www/secure 
  • apche再起動
# /etc/init.d/httpd restart
  • EC2のSecurityGroupでHTTPSを開ける
  • httpsでアクセスできるかテスト
https://ホストですがな/redmine
  • /redmineと/jenkinsはhttpでアクセスがあったらhttpsに飛ばす設定にする
/etc/httpd/conf.d/rewire.conf を以下の内容で作成
<IfModule mod_rewrite.c>
      RewriteEngine On
      RewriteLog "logs/rewrite_log"
      RewriteLogLevel 0
      RewriteCond %{SERVER_PORT} !^443$
      RewriteRule ^/redmine https://%{HTTP_HOST}/redmine [L,R]
      RewriteRule ^/jenkins https://%{HTTP_HOST}/jenkins [L,R]
</IfModule>
  • apache再起動
# /etc/init.d/httpd restart
  •  ちゃんとrewriteされるかをテスト
http://ホストですがな/redmine
http://ホストですがな/jenkins
でアクセスしたら
https://ホストですがな/redmine
https://ホストですがな/jenkins
になればOK。

redmineとjenkinsの仕込み #3(jenkins on glassfish編)

#2の続き。
jenkinsを仕込む。アプリケーションサーバはglassfishを使う。
glassfishを選択した理由は何となく。


11. JDK7を入れる

  • Linux x64のtarballをsunじゃなくてOracleのサイトからダウンロード、/usr/local に展開。
  • #ln -s /usr/local/jdk1.7.0_09 /usr/local/java (←いつもデフォルトのJAVA_HOMEをこれにしている)
  • JAVA_HOMEを /usr/local/java にする
  • PATHの先頭に $JAVA_HOME/bin を入れる

13. glassfish実行ユーザを作成する

rootで動かすのはちょっとね。
# adduser glassfish

14 .glassfishを入れる

  • multilingualインストーラーパッケージをget
#wget http://download.java.net/glassfish/3.1.2/release/glassfish-3.1.2-unix-ml.sh
  • CUIインストールのためにanswerファイルをあらかじめ作る
InstallHome.directory.INSTALL_HOME=/usr/local/glassfish-3.1.2
License.license.ACCEPT_LICENSE=0
RegistrationOptions.regoptions.CREATE_NEWACCT=CREATE_NEWACCT
RegistrationOptions.regoptions.DUMMY_PROP=
RegistrationOptions.regoptions.SKIP_REGISTRATION=SKIP_REGISTRATION
RegistrationOptions.regoptions.USERNAME=
RegistrationOptions.regoptions.USERPASSWORD=
RegistrationOptions.regoptions.USE_EXISTINGACCT=USE_EXISTINGACCT
SOAccountCreation.accountinfo.COMPANYNAME=
SOAccountCreation.accountinfo.COUNTRY=
SOAccountCreation.accountinfo.COUNTRY_DROP_DOWN=
SOAccountCreation.accountinfo.EMAIL=
SOAccountCreation.accountinfo.FIRSTNAME=
SOAccountCreation.accountinfo.LASTNAME=
SOAccountCreation.accountinfo.PASSWORD=
SOAccountCreation.accountinfo.REENTERPASSWORD=
glassfish.Administration.ADMIN_PASSWORD=管理者パスワード
glassfish.Administration.ADMIN_PORT=4848
glassfish.Administration.ADMIN_USER=管理者ユーザ名
glassfish.Administration.ANONYMOUS=ANONYMOUS
glassfish.Administration.LOGIN_MODE=true
glassfish.Administration.HTTP_PORT=8888
glassfish.Administration.NON_ANONYMOUS=NON_ANONYMOUS
updatetool.Configuration.ALLOW_UPDATE_CHECK=true
updatetool.Configuration.BOOTSTRAP_UPDATETOOL=true
updatetool.Configuration.PROXY_HOST=
updatetool.Configuration.PROXY_PORT=
  • インストーラー実行
# sh ./glassfish-3.1.2-unix-ml.sh -a answer -s
SETTING UP DOMAIN FOR SILENT INSTALL...
Executing /usr/local/glassfish-3.1.2/glassfish/bin/asadmin --user admin --passwordfile - create-domain --savelogin --checkports=false --adminport 4848 --instanceport 8080 --domainproperties=jms.port=7676:domain.jmxPort=8686:orb.listener.port=3700:http.ssl.port=8181:orb.ssl.port=3820:orb.mutualauth.port=3920 domain1
/usr/local/glassfish-3.1.2/glassfish/bin/asadmin --user admin --passwordfile - create-domain --savelogin --checkports=false --adminport 4848 --instanceport 8080 --domainproperties=jms.port=7676:domain.jmxPort=8686:orb.listener.port=3700:http.ssl.port=8181:orb.ssl.port=3820:orb.mutualauth.port=3920 domain1 Adminのポート4848を使用しています。
HTTP Instanceのポート8080を使用しています。
JMSのポート7676を使用しています。
IIOPのポート3700を使用しています。
HTTP_SSLのポート8181を使用しています。
IIOP_SSLのポート3820を使用しています。
IIOP_MUTUALAUTHのポート3920を使用しています。
JMX_ADMINのポート8686を使用しています。
OSGI_SHELLのデフォルト・ポート6666を使用しています。
JAVA_DEBUGGERのデフォルト・ポート9009を使用しています。
指定されたロケール[ja_JP]のファイルが[/usr/local/glassfish-3.1.2/glassfish/lib/templates/locales/ja_JP/index.html]に見つかりませんでした。デフォルト(en_US)のindex.htmlをかわりに使用します。
自己署名付きX.509サーバー証明書の識別名です:
[CN=ip-10-132-18-23.ap-northeast-1.compute.internal,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US]
自己署名付きX.509サーバー証明書の識別名です:
[CN=ip-10-132-18-23.ap-northeast-1.compute.internal-instance,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US]
ドメインの初期化子が見つかりません。カスタマイズの手順を省略します
ドメインdomain1が作成されました。
ドメインdomain1の管理ポートは4848です。
ドメインdomain1によって、パスワードなしでユーザー"admin"として管理ログインできます。
このドメイン[domain1]の管理ユーザー名[admin]
に関連するログイン情報が
[/root/.asadminpass]に正常に格納されました。
このファイルが保護されたままであることを確認します。
このファイルに格納された情報は、
このドメインを管理するためにasadminコマンドによって使用されます。
コマンドcreate-domainは正常に実行されました。
  • というわけで、ADMIN_USER,ADMIN_PASSWORD,HTTP_PORTとかがなんだか無視されてるみたいだ。「ドメインの初期化子が見つかりません。カスタマイズの手順を省略します」ってのがどうアレみたい。うーんまあいいや。
  • glassfishユーザのモノにする
# chown -R glassfish:glassfish /usr/local/glassfish-3.1.2
  • EC2のSecurity Groupで、4848を開ける。
    8080は空けないで、ajpを使ってapacheを通して公開することにする。(そうしなければならない理由もないけどね)
  • domain1を起動
glassfishユーザになる
.bashrcで/usr/local/glassfish-3.1.2/bin をPATHに追加
$ asadmin start-domain domain1
  • 管理コンソールをSSLにする
4848にアクセスすると
Secure Admin must be enabled to access the DAS remotely.
って言われる。リモートからの管理コンソール利用はenable-secure-admin を指定しないといけないと。
コンソールで以下のコマンドを実行する
# asadmin --host localhost --port 4848 enable-secure-admin
remote failure: 少なくとも1つの管理ユーザーに空のパスワードがありますが、セキュ リティ保護された管理では許可されません。change-admin-passwordコマンドまたは管理 コンソールを使用して、空でないパスワードを管理アカウントに作成してください。
って言われるので、
$ asadmin --host localhost --port 4848 change-admin-password
でパスワードを設定。そしてやり直し。
$ asadmin --host localhost --port 4848 enable-secure-admin
  • domain1 を再起動してアクセスの確認 
# asadmin restart-domain domain1
これで http://インストールしたホスト:4848 にアクセスすると https://インストールしたホスト:4848 にリダイレクトされるようになる。ログインできることを確認。 
  • rootに戻って起動スクリプトを作る /etc/init.d/glassfish-domain1
#!/bin/bash
#
# glassfish:          Startup script for Glassfish Application Server.
#
# chkconfig: 3 80 05
# description:      Startup script for domain1 of Glassfish Application Server.
GLASSFISH_HOME=/usr/local/glassfish-3.1.2
export GLASSFISH_HOME
JAVA_HOME=/usr/local/java
export JAVA_HOME
GLASSFISH_OWNER=glassfish
export GLASSFISH_OWNER
start() {
        echo -n "Starting Glassfish: "
        su $GLASSFISH_OWNER -c "$GLASSFISH_HOME/bin/asadmin start-domain domain1
> /dev/null 2>&1"
}
stop() {
        echo -n "Stopping Glassfish: "
        su $GLASSFISH_OWNER -c "$GLASSFISH_HOME/bin/asadmin stop-domain domain1
> /dev/null 2>&1"
}
restart() {
        echo -n "Stopping Glassfish: "
        su $GLASSFISH_OWNER -c "$GLASSFISH_HOME/bin/asadmin restart-domain domai
n1 > /dev/null 2>&1"
}
# See how we were called.
case "$1" in
        start)
                start
                ;;
        stop)
                stop
                ;;
        restart)
                stop
                start
                ;;
        *)
                echo $"Usage: glassfish {start|stop|restart}"
                exit
esac
  • OS起動時設定
# chkconfig glassfish-domain1 on

15. jenkinsをデプロイする

  • jenkins.warをhttp://jenkins-ci.org/ からgetする
  • glassfishの管理コンソールにログイン
https://ホストですがな:4848
  • jenkins.warを deployする

16. mod_proxy_ajpでapache連携するための設定

  • またglassfishユーザになって以下のコマンド
$ asadmin create-network-listener --listenerport 8009 --protocol http-listener-1 --jkenabled true jk-connector
  • rootに戻る 
  • /etd/httpd/conf.d/ajp.conf を作る
<Location /jenkins>
  ProxyPass ajp://localhost:8009/jenkins
</Location>
  • httpdを再起動
  • http://ホストですがな/jenkins にアクセスしてみる

17. Jenkinsのセキュリティを有効化

  • Jenkinsの管理 > ユーザーの管理 で管理者用ユーザを作る
  • Jenkinsの管理 > システムの設定 でセキュリティを有効にする
  • 『セキュリティの有効化』をチェック アクセス制御は『enkinsのユーザーデータベース』を選択 権限管理は『行列による権限設定』を選択
  • さっき作った管理者ユーザに権限を付与
  • 匿名ユーザから全権限を削除



今日はここまで。


2012年11月8日木曜日

redmineとjenkinsの仕込み #2(redmine編)

#1の続き。 redmine + backlogsをインストール。


6. ruby のインストール

  • まずyamlを入れる 
 # wget http://pyyaml.org/download/libyaml/yaml-0.1.4.tar.gz
展開してそのディレクトリへ移動
# configure
# make
# make install
  • ruby 1.9.3 のインストール
# wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p286.tar.gz
# configre
# make
# make install
  • bundlerのインストール
gem install bundler --no-rdoc --no-ri

7. mysqlセットアップ

  • /etc/my.cnf編集
# Settings user and group are ignored when systemd is used.
# If you need to run mysqld under different user or group,
# customize your systemd unit file for mysqld according to the
# instructions in http://fedoraproject.org/wiki/Systemd
[mysqld]
datadir=/vol1/mysql
socket=/var/lib/mysql/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# utf8
character-set-server=utf8
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
[mysql]
default-character-set=utf8
  • mysqldの起動とOS起動時設定
/etc/init.d/mysqld start
chkconfig mysqld on
  • 一応キャラクタセットを確認
# mysql -uroot
mysql> show variables like 'character_set%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
  • rootユーザのパスワード設定
mysql> use mysql;
mysql> update user set password=password('*******') where user='root';
mysql> delete from user where user='';
mysql> flush privileges;
  • redmineデータベース作成
mysql> create database redmine default character set utf8;
mysql> grant all on redmine.* to redmine@localhost identified by '*******';
mysql> flush privileges;
  • mysqlのCバインディングをインストール
# gem install mysql2

8. redmineのインストール

  • redmine2.0.4 をget
# wget http://rubyforge.org/frs/download.php/76444/redmine-2.0.4.tar.gz
  • /usr/localに展開
  • config/database.yml を作る
production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: ********
  encoding: utf8
  • config/configuration.yml を作る
default:
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      address: "localhost"
      port: 25
  • Gemパッケージのインストール
redmineインストールディレクトリで
#bundle install --without development test postgresql sqlite
  • Redmineの初期設定とデータベースのテーブル作成
redmineインストールディレクトリで
# rake generate_secret_token
# RAILS_ENV=production rake db:migrate
# RAILS_ENV=production rake redmine:load_default_data
  • Passengerのインストール
# gem install passenger --no-rdoc --no-ri
  • PassengerのApache用モジュールのインストール
# passenger-install-apache2-module

9.Apacheの設定

  • /etc/httpd/conf.d/passenger.conf を作成
RackBaseURI /redmine
LoadModule passenger_module /usr/local/lib/ruby/gems/1.9.1/gems/passenger-3.0.18/ext/apache2/mod_passenger.so
PassengerRoot /usr/local/lib/ruby/gems/1.9.1/gems/passenger-3.0.18
PassengerRuby /usr/local/bin/ruby
PassengerMaxPoolSize 20
PassengerMaxInstancesPerApp 4
PassengerPoolIdleTime 3600
PassengerUseGlobalQueue on
PassengerHighPerformance on
PassengerStatThrottleRate 10
PassengerSpawnMethod smart
RailsAppSpawnerIdleTime 86400
RailsFrameworkSpawnerIdleTime 0
  • redmineをドキュメントルートにシンボリックリンク(/redmineでアクセスできるように)
# ln -s redmineのインストールディレクトリ/public /apcheのドキュメントルート/redmine
  • apache上で動かすのでオーナーをapacheにする
# chown -R apache:apache redmineのインストールディレクトリ
  • apacheの設定チェックと再ロードとOS起動時設定
# /etc/init.d/httpd configtest
# /etc/init.d/httpd graceful
# chkconfig httpd on
  • http://仕込んだサーバ/redmie にアクセスして確認
  • adminのパスワードを変える
  • adminじゃない管理ユーザを作る
  • 作った管理ユーザでログインしてadminをロックする

10. backlogsを入れる

基本的にbacklogsサイトの手順に従えばOK。
  • backlogsをget
pluginsディレクトリで
# git clone git://github.com/backlogs/redmine_backlogs.git
# cd redmine_backlogs
# git tag (バージョンのリストを確認)
# git checkout v0.9.26 (最新をチェックアウト)
  • XML関係のライブラリをインストール
# yum install -y libxml2 libxml2-devel libxslt libxslt-devel
  • bundle install
# bundle install --without development test postgresql sqlite
You cannot specify the same gem twice with different version requirements. You specified: test-unit (>= 0) and test-unit (= 1.2.3)
と言われる。
redmine_backlogsのtest-unitは
gem "test-unit", "=1.2.3" if RUBY_VERSION >= "1.9"
となっていて、1.2.3を指定しているが、redmine本体の方のGemfileはそれがない。
本体の方のGemfileでも"=1.2.3"を指定するように書き換えて再実行。
  • holydaysをインストール
holidaysは1.0.3を入れてからじゃないと調子悪いらしい
# gem install holidays --version 1.0.3
# gem install holidays
  • 環境変数RAILS_ENVを設定しちゃう
export RAILS_ENV=production
  • マイグレート
# bundle exec rake db:migrate
  • おまじない
# bundle exec rake tmp:cache:clear
# bundle exec rake tmp:sessions:clear
  • インストール
# bundle exec rake redmine:backlogs:install
  •  オーナーをapacheに
# chown -R apache:apache redmineのインストールディレクトリ
  • httpdを再起動
# /etc/init.d/httpd restart 
  • redmineにアクセスしてbacklogsがインストールされていることを確認


今日はここまで。