Maven2社内利用向けツール Apache Archiva

Maven2を社内で利用する場合に、色々な不都合が生じるかと思います。私のケースで言えば…

  1. 社内リポジトリ環境構築 (WebDAVFTP?)
  2. 社内リポジトリへのライブラリの重複登録
  3. セントラルリポジトリ(およびそれのミラー)へ各々がアクセス

こんなところでしょうか。1についてはTomcat5.5では標準でWebDAV環境があるため、サーバ側はそれを使い、クライアントはhttp://docs.codehaus.org/display/MAVENUSER/Deploying%203rd%20Party%20Jars%20With%20WebDAVに記載の方法でmaven2からWebDAV経由で社内リポジトリにデプロイする方法を使いました。

ですが、さすがに2と3は解決できない。やっぱり無駄は省きたいですよね。
ってなところに、1,2,3すべて解決できそうなツールを発見しました。

Archiva – The Build Artifact Repository Manager

これは簡単に言うとMavenリポジトリのプロキシみたいなもののようです。代表的な機能としては、アーティファクトのキャッシュ、デプロイ用WebDAV環境の提供、そしてキャッシュされているアーティファクトの検索が可能になるツールです。これで http://mvnrepository.com/ がたまに落ちていたりなんかしてもキャッシュされている分の再検索は可能ですね。

とりあえず簡単にhttp://archiva.apache.org/docs/1.0.2/quick-start.htmlをご覧下さい。ちょっと試すだけであれば、war形式よりもStandalone版をダウンロードした方がいいです。Standalone版の方が、依存ライブラリのDerbyやMailライブラリも入っているし、コマンド一発で起動することもできるからです。更にはWindowsのサービスとして起動させる機能もあるようですね。

Archivaは以下のコマンドでコマンドプロンプト版の起動ができます。

./bin/linux-x86-32/run.sh console
 or
.\bin\windows-x86-32\run.bat console

アーティファクトのキャッシュを行いたい場合はhttp://archiva.apache.org/docs/1.0.2/userguide/using-repository.htmlを、アーティファクトのデプロイを行いたい場合は、http://archiva.apache.org/docs/1.0.2/userguide/deploy.htmlをご参照下さい。

本当はポインタだけでなく、もう少し踏み込んだ説明を行いたいのですが、自分もまだ不勉強&まだ導入できていないので…すみません。

フォームの複数の要素をまとめて1つのパネルにする方法

RDBテーブルのカラムのデータ型に合った検索条件入力部を作ろう!となったときに、データ型に合った条件を入力できるパネルがあればいいなと思いました。
(例えば、文字列での検索の場合は条件と部分一致か完全一致かのプルダウンを付ける、とか、日付の場合、From条件、To条件およびそれらにカレンダーを付与、とか)

このような処理の場合、条件を入力できる部分が複数個あっても、返す値(オブジェクト)は一つである、ということがキモになるかと思います。あまり個々の値を返されても嬉しくないと思うので、パネル内で入力された値をもとに、1つの値(オブジェクト)を返してしまうのがよいでしょう。

方法としては、http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/markup/html/form/FormComponentPanel.htmlを使用します。

例えば、文字列検索を行うようなパネルを作るとします。
HTML側は以下の様に、wicket:panelタグで括ります。

<wicket:panel>
  <select wicket:id="condition">
    <option value="0">一致条件</option>
  </select>
  <input type="text" wicket:id="query" />
</wicket:panel>

対応するJavaは以下のような感じでしょうか。

public class StringCondPanel extends FormComponentPanel {
    private DropDownChoice condComponent;
    private TextField queryComponent;
    
    private int condition = 1;
    private String query;
    
    private static final List<Integer> CONDITIONS = Arrays.asList(
        new Integer[]{Integer.valueOf(1),Integer.valueOf(2),Integer.valueOf(3)});

    public FieldCondPanel(String id) {
        super(id);
        init();
    }

    public FieldCondPanel(String id, IModel model) {
        super(id, model);
        init();
    }

    private void init() {
        condComponent = new DropDownChoice("condition", new PropertyModel(this, "condition"), CONDITIONS);
        condComponent.setRequired(true);
        //1,2,3に完全一致、部分一致、不一致をマッピングする
        condComponent.setChoiceRenderer(new IChoiceRenderer() {
            public Object getDisplayValue(Object object) {
                int value = (Integer)object;
                String result;
                switch (value) {
                    case 1: result = "完全一致"; break;
                    case 2: result = "部分一致"; break;
                    default: result = "不一致"; break;
                }
                return result;
            }

            public String getIdValue(Object object, int index) {
                return String.valueOf(CONDITIONS.get(index));
            }
            
        });
        add(condComponent);
        
        add(queryComponent = new TextField("query", new PropertyModel(this, "query")));
    }

    public int getCondition() {
        return condition;
    }

    public void setCondition(int condition) {
        this.condition = condition;
    }

    public String getQuery() {
        return query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    @Override
    protected void convertInput() {
        //検索条件と一致条件を取得して自前でセット
        query = (String) queryComponent.getConvertedInput();
        condition = (Integer) condComponent.getConvertedInput();
        //条件を生成してください
        String strCond = ""; //ここに条件を書くか、専用のクラスを作っておく
        //IModel.getObject()で取得したい値をセットする
        setConvertedInput(strCond);
    }
}

ここで大事なのは、convertInput()メソッドです。convertInput()メソッド内で、ポストされてきた条件を受け取り、必要な値(オブジェクト)に加工して、setConvertedInput(obj)でセットする必要があります。
また、私がハマッたのですが、queryとconditionはCompoundPropertyModelで値がセットされるから、queryComponent.getConvertedInput()で値を取得しなくてもいいじゃないかと思い、省略してコンパイル、実行したところ、setConvertedInputには予期せぬ値が入っていました。

原因は、デバッガ等で追いかけたところによると、CompoundPropertyModelによって値をセットするよりも前に、convertInput()メソッドが呼ばれてしまうためです。その結果、queryComponent, condComponentにはフォームからの値が入っていますが、query, conditionには値が入っていない、という事態になるわけです。

(ちなみに、*Component.getConvertedInput()が呼ばれたものについては、convertInput()メソッドの実行後に呼ばれるCompoundPropertyModelによる値のセットは省略されるようです。呼ばれていないものについては値をセットします。)

さて、肝心の条件部分の生成は置いておいてしまいましたが、何とか複数の入力を1つにまとめるパネル作りは何とかなりそうです。
次の目標は、上記のようなパネルをループで複数個生成できるのか、そして複数個の値を保持できるModelはあるのか、要調査です。

参考ページ http://www.wicket-library.com/wicket-examples/forminput

http://cappuccino.jp/scala-ja/?Scala%CA%D9%B6%AF%B2%F1%A1%F7%B4%D8%C0%BE-1に行ってきた

Scalaにちょっと興味が出てきたので、勉強会に行ってきた次第です。ちなみに勉強会へは初参加。

とりあえずすっきりしたことをつらつらと。

メソッドと関数(オブジェクト)は違う

クロージャを作る際に、ScalaのREPL上で以下のようなことを行うとエラー。理由は、実はTopオブジェクトみたいなのがいるらしいから。で、メソッドを関数オブジェクトにする場合は、(メソッド名 _) とすればよい。
また、メソッドを関数オブジェクト化した場合、フィールド変数などはちゃんと参照可能になっています。詳しくは調べていませんが、Schemeクロージャのように、レキシカルスコープなのかしら。

scala> def f(x:Int) = {x + 3}
f: (Int)Int

scala> var ff = f
<console>:5: error: missing arguments for method f in object $iw;
follow this method with `_' if you want to treat it as a partially applied function
       var ff = f
                ^

scala> var ff = (f _)
ff: (Int) => Int = <function>

scala> class A(_x:Int) {var x = _x}
defined class A

scala> var a = new A(3)
a: A = A@135b95d

scala> (a.x _)
res4: () => Int = <function>

scala> res4()
res5: Int = 3

1 to 5 map fizzBuzz は、(1.to(5)).map((x:Int) => {fizzBuzz(x)})

1はInt型、toはメソッド、5は引数、…となっているのですが、Intにはtoメソッドがない。これはRichIntで定義されており、IntがRichIntのメソッドを利用できるのは、Implicit Conversionという機能によるものだそうです。

その他雑感

  • 型推論とカリー化はまだまだ発展の余地あり?
  • 正規表現リテラルでほすぃ
  • Actorはスレッドプールで実現されているため、スケールは難しいんじゃね?的な話

その後

その場で懇親会が開かれたのですが、みんな凄いね。

  • 何故か化学の話になったり、
  • 何故かスカラー波の話になったり、
  • 何故かノートPCの電源効率の話になったり、
  • 何故か交通手段の効率の話になったり、
  • 何故か宇宙線のちかr(ry

みんなGJ。
個人的には、ついったにも書いたけれど、電車は自動車と違ってある程度移動する方向を制限する代わりにスケールするから電力的な効率がいいってことかな。

えーと何の勉強会でしたっけ。あぁそうだった。スケール重要っていう勉強会でした。
以上。

追記というか大事なこと

keisuke_nさん、講師お疲れ様でした。非常に分かりやすかったです。

また、勉強会には参加されていない方でしたが、勉強会メンバに差し入れをいただきました。ありがとうございました。

Scalaのブロック構文、関数、メソッド

昨日の投稿は、関数型っぽくと書いたけれど、どちらかと言うとRubyのブロック構文のようなイメージでした。

関数型っぽく書くとなると、defで関数(なのかメソッドなのか)を定義するのがよいのかと思った次第。
昨日とは違い、REPL上で実施。

scala> def fizzBuzz(x:Int):String = {
  var ret:String = ""
  if (x % 3 == 0) ret = "Fizz"
  if (x % 5 == 0) ret += "Buzz"
  if (ret.length == 0) ret = x.toString
  ret
}
fizzBuzz: (Int)String

scala> 1 to 20 map fizzBuzz  //ここで関数fizzBuzzを引数なしで渡している
res17: RandomAccessSeq.Projection[String] = RangeM(1, 2, Fizz, 4, Buzz, Fizz, 7,
 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz)

scala> (fizzBuzz _).getClass
res18: java.lang.Class[?0] forSome { type ?0 } = class $anonfun$1

ちなみに最後の行は、fizzBuzzのクラスが何かを調べる方法。単純にfizzBuzz.getClass ではエラーが返るので途方に暮れていたところ、id:kmizushima (@kmizu)さんからTwitter経由で教えていただきました。ありがとうございます。

で、何故 _ が必要なのか、そして$anonfun$1 とは何者か、が疑問に。


また、すっきりしないのが、ブロック構文と関数とメソッドの違い。まだよく調べていないんだけど、何か微妙に様子が異なる?今日中に考えをまとめて、明日聞くことにしよう。

JJUG CCCおよびid:happy_ryo迎撃オフの件もgdgdになって書けなくなったので、ScalaでFizzBuzzを関数型っぽく書いてみる。

タイトル長い。

関数型っぽく、Intのリストを作り、それをStringのリストに変換し、そのリストに対してprintlnで出力するだけの簡単なお仕事です。

object FizzBuzz {
  def main(args : Array[String]) {
    val list = 1 to 100
    val fizzbuzz = list.map ((x:Int) => {
        var ret:String = ""
        if (x % 3 == 0) ret += "Fizz"
        if (x % 5 == 0) ret += "Buzz"
        if (ret.length == 0) ret = x.toString
        ret
      })
      fizzbuzz.foreach ((x) => {
        println(x)
      })
  }
}

というわけで、5/10(土)に開催のhttp://cappuccino.jp/scala-ja/?Scala%CA%D9%B6%AF%B2%F1%A1%F7%B4%D8%C0%BE-1に参加させていただきます。

日本Javaユーザーグループ

日本Javaユーザーグループ
に行けることになりました。技術動向調査、という仕事目的です。

java-jaの方々にお会いできるかな?ちょっと楽しみ。
本命はもちろん真面目でかっこいい世界のid:t_yanoのお話ですかね! (Google suggest対策)

一番簡単なValidationおよびStatelessForm + validationはダメよのお話

一番簡単なValidationと言えば、必須チェック。タイトルを必須にする対応を行います。
各フォームのコンポーネントhttp://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/markup/html/form/FormComponent.htmlのサブクラスのようです。ですので、setRequired(true)とすることで、必須チェックを行うことが出来ます。

…ただ、StatelessFormに対しては、必須チェックを行ってはいけないようです。
最初にBodyに"test"とだけ入力し、POSTすると必須チェックのメッセージが表示されます。そこでタイトルにも"title"と入力してPOSTすると。

あるぇ〜?まだ怒られるし、何か入力が変だし。

1日ほどずっとハマッていたわけですが、原因はStatelessFormにあるようです。単なるFormにすると直りました。うーん、状態を保持しないから、Validationに引っかかった場合の状態の管理が変になったという感じなのでしょうか…?
ちょっとソースを追いかけないと分かりません。分かりませんが、StatelessFormは名前の通り状態を保持しないフォームクラスです。これは、たとえばGoogleはてなの検索窓のように、データの登録ではなく検索系などに使うのが本来の使い方なのだと思います。あくまで予想ね。