Usa*Usa日記 このページをアンテナに追加 RSSフィード

2006 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 05 | 06 |
2013 | 05 |

2012-01-03

[]Smart Object Query(JavaLINQ to Objectsモドキ私家版)公開

昨年末の気分は既に長期休暇モードな時期に、手慰みで作っていたライブラリを公開してみました(・∀・)

https://github.com/usausa/Java-Smart-ObjectQuery

なんぞこれ(・ω・)?

.NETもJavaも仕事でやっている人間なら一度は実装を試みる、LINQ(to Objects)っぽい感じのコレクション操作ライブラリの私家版です。

実用性はおいといて、LINQ(to Objects)ってどういうものよ?、っというのをJavaなりの解釈で実装するとどうなるか、っということをやってみた感じです(・ω・) *1

そもそも

そもそもLINQ to Objectsって使うの(・ω・)?、っという話がありますが。

こういう意見が出る背景としては、

  • 実行効率を考えればクエリはエッジ(データストア、もっというとRDB内)でやるべきものじゃないの(・ω・)?

っというものがあると思います。

勿論、それは当然とした上で、

みたいな話もあるわけで。


例えばRDBのようなRowセットに対して、画面も単純な表形式であれば単純に処理を記述できますが。

オサレなデザインや分析系の画面などで、動的な縦横変換やデータ間の関係を考慮した描画が必要になったり、そこに権限とかその他要素諸々が絡んできたりすると、データストア上のデータ構造そのままを使って描画処理を記述しようとすると、処理の記述が煩雑になってきたりします(・ω・;)

そこで、データストア上のデータ構造をプレゼンテーション層用のデータ構造に変換して、描画処理の記述を簡潔にしようとしたりするわけで、自分は、この手の処理にLINQ(to Object)をよく使用します(・∀・)*2


あと、データストア上でのクエリをこねくり回して、プレゼンテーション層用のデータ構造でデータを取得することも出来るんじゃないの(・ω・)?、っという話もありますが。

それは責務が違うし、プレゼンテーションが複雑になればなるほど破綻するので、自分としては、

っというパターン押しです(`・ω・´)

本題/使い方

っで、Smart Object Queryについてですが、テストコードもちゃんと用意していないので、ここに少し使い方を書いておきます。

処理一覧
メソッド分類種別Iterator
fromArrayクエリ始点(ArrayList)
from 始点(元のIterator)
fromEnumeration 始点EnumerationIterator
fromEmpty 始点EmptyIterator
fromSingle 始点SingleIterator
repeat集合生成始点RepeatIterator
range 始点RangeIterator
rownumインデックス付加中途IndexedIterator
defaultIfEmpty空集合中途DefaultIfEmptyIterator
conact集合演算結合ConactIterator
distinct 中途DistinctIterator
union 結合UnionIterator
expect 結合ExpectIterator
intersect 結合IntersectIterator
zip平行アクセス結合ZipIterator
skipページング中途SkipIterator
skipWhile 中途SkipWhileIterator
take 中途TakeIterator
takeWhile 中途TakeWhileIterator
orderソート再評価(ArrayList)
reverse 再評価(ArrayList)
all限量詞演算終端-
any 終端-
contains 終端-
first取得終端-
last 終端-
elementAt 終端-
count集約演算終端-
aggregate 終端-
sum 終端-
max 終端-
min 終端-
average 終端-
where選択中途WhereIterator
groupByグループ化中途GroupedIterator
select射影中途SelectIterator
selectMany 中途SelectManyIterator
join結合結合JoinIterator
groupJoin 結合GroupJoinIterator
toList変換終端-
toSet 終端-
toMap 終端-
toLookup 終端-
each評価副作用-

メソッド」はObjectQueryクラスのメソッドです。

引数のSelector等の違いによる、複数定義があるものもあります。


「分類」はざっくりとした分類です(・∀・;)


「種別」は、そのメソッドIteratorのチェインの始点/途中/終端のどれかについての分類です。


Iterator」はその処理を実装するIteratorです。

(ArrayList)のような記述は、そのクラスを利用して処理を実装しているものです。

基本

基本ということで、Listに入った0〜9の数値のうち、偶数だけを取得するような処理の記述

List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
    list.add(i);
}

for (Integer value : ObjectQuery.from(list).where(new Predicate<Integer>() {
    @Override
    public boolean test(final Integer param) {
        return param % 2 == 0;
    }})) {
    System.out.println(value);
}
[実行結果]
0
2
4
6
8

ラムダが無いとPredicateの記述が冗長ですよね。

っということで、よく使用する関数セレクタは、専用のクラスへ定義を集約したり、Beanのstaticメンバなんかに定義しておくのもありだと思います。

public class Functions {

    public static final Predicate<Integer> EVEN = new Predicate<Integer>() {
        @Override
        public boolean test(final Integer param) {
            return param % 2 == 0;
        }
    };
}

を用意しておいて、

for (Integer value : ObjectQuery.from(list).where(Functions.EVEN)) {
    System.out.println(value);
}

とか。

ページング

次のサンプルはページングということで、まずデータ用のクラスとして下記のようなものを用意して。

public class Data {

    private int id;

    private String data;

    public int getId() {
        return id;
    }

    public String getData() {
        return data;
    }

    public Data(final int id, final String data) {
        this.id = id;
        this.data = data;
    }

    @Override
    public String toString() {
        return "{ id = [" + id + "], data = [" + data + "] }";
    }
}

ついでにデータの作成もObjectQueryを使ってみたりして。

Iterable<Data> datas = ObjectQuery.range(1, 10).select(new Func1<Integer, Data>() {
    @Override
    public Data eval(final Integer id) {
        return new Data(id, "Data-" + id);
    }
});

for (Data data : datas) {
    System.out.println(data);
}
[実行結果]
{ id = [1], data = [Data-1] }
{ id = [2], data = [Data-2] }
{ id = [3], data = [Data-3] }
{ id = [4], data = [Data-4] }
{ id = [5], data = [Data-5] }
{ id = [6], data = [Data-6] }
{ id = [7], data = [Data-7] }
{ id = [8], data = [Data-8] }
{ id = [9], data = [Data-9] }
{ id = [10], data = [Data-10] }

あと、表示についてはeach()メソッドを使用してこんな風にも書けたりとか。

ObjectQuery.from(datas).each(new Action<Data>() {
    @Override
    public void run(final Data data) {
        System.out.println(data);
    }
});

っで、本題のページングですが、

ObjectQuery
    .from(datas)                            // datasから
    .order(new Func1<Data, Integer>() {     // id逆順でソートして
        @Override
        public Integer eval(final Data data) {
            return - data.getId();
        }
    })
    .skip(5)                               // 5件飛ばしから
    .take(2)                               // 2件取得したものを
    .each(new Action<Data>() {             // 表示
        @Override
        public void run(final Data data) {
            System.out.println(data);
        }
    });
[実行結果]
{ id = [5], data = [Data-5] }
{ id = [4], data = [Data-4] }

みたいな感じで。

取得

条件にマッチする最初のデータを取得。

Data data = ObjectQuery.from(datas).first(new Predicate<Data>() {
    @Override
    public boolean test(final Data data) {
        return data.getId() % 2 == 1;
    }
});
System.out.println(data);
[実行結果]
{ id = [1], data = [Data-1] }

条件にマッチする最後のデータを取得。

Data data = ObjectQuery.from(datas).last(new Predicate<Data>() {
    @Override
    public boolean test(final Data data) {
        return data.getId() % 2 == 1;
    }
});
System.out.println(data);
[実行結果]
{ id = [9], data = [Data-9] }

first()、last()メソッドは、本家におけるFirstOrDefault()、LastOrDefault()に対応するもので、データが無い時に例外を投げるバージョンは用意していません。

なぜなら、自分がそのパターンをあまり使わないから(・∀・;)

関連して、single()も用意していなかったり。

判定処理

コレクション中の値について、全ての値が条件を満たすかチェックするall()と、条件を満たすものが一つでもあるかチェックするany()について。

便宜上下記のようなメソッドを用意しておいて。

public static class Functions {

    public static final Predicate<Integer> lessThan(final int compare) {
        return new Predicate<Integer>() {
            @Override
            public boolean test(final Integer value) {
                return value < compare;
            }
        };
    }

    public static final Predicate<Integer> greaterThan(final int compare) {
        return new Predicate<Integer>() {
            @Override
            public boolean test(final Integer value) {
                return value < compare;
            }
        };
    }
}

要素全てが条件を満たす判定処理all()と、要素の何れかが条件を満たす判定処理any()の使用方法は下記のような感じで。

// 1..10は全て11未満
assertTrue(ObjectQuery.range(1, 10).all(Functions.lessThan(11)));
// 1..10は全て10未満では無い
assertFalse(ObjectQuery.range(1, 10).all(Functions.lessThan(10)));

// 1..10は何れかが9より大きい
assertTrue(ObjectQuery.range(1, 10).any(Functions.greaterThan(9)));
// 1..10は何れかが10より大きくない
assertFalse(ObjectQuery.range(1, 10).any(Functions.greaterThan(10)));
集計関数

件数を取得するcount()、最大値max()、最小値min()、平均値average()、汎用/内部的に使用しているaggregate()とか。

Javaの都合上、戻り値の型毎にmaxLong()、maxDouble()等のバリエーションを用意(・ω・;)


っで、使い方の前に、Selectorの関数を以下のように用意しておいて。

public class Data {

    private int id;
...
    public static Func1<Data, Integer> ID_SELECTOR = new Func1<Data, Integer>() {
        @Override
        public Integer eval(final Data data) {
            return data.getId();
        }
    };
}

以下、使用方法。

int count = ObjectQuery.from(datas).count(new Predicate<Data>() {
    @Override
    public boolean test(final Data data) {
        return data.getId() > 5;
    }
});
System.out.println(count);
int max = ObjectQuery.from(datas).max(Data.ID_SELECTOR);
System.out.println(max);
int min = ObjectQuery.from(datas).min(Data.ID_SELECTOR);
System.out.println(min);
[実行結果]
5
10
1

なお、int配列に対する処理とかは、ObjectQueryではなくIntegersクラスとかに実装するような方針なのです(・ω・)

集合演算

集合演算(和、積、差)について。

使用するデータは下記だとして。

List<Integer> list = Arrays.asList(1, 3, 2, 3);

List<Integer> list1 = Arrays.asList(1, 3, 2);
List<Integer> list2 = Arrays.asList(3, 4);

重複削除。

for (Integer value : ObjectQuery.from(list).distinct()) {
    System.out.println(value);
}
[実行結果]
1
3
2

和。

for (Integer value : ObjectQuery.from(list1).union(list2)) {
    System.out.println(value);
}
[実行結果]
1
3
2
4

積。

for (Integer value : ObjectQuery.from(list1).intersect(list2)) {
    System.out.println(value);
}
[実行結果]
3

差。

for (Integer value : ObjectQuery.from(list1).expect(list2)) {
    System.out.println(value);
}
[実行結果]
1
2
結合

次は結合処理について(・ω・)

まず、以下のようなデータクラス(トランザクション系およびマスタ系だと思いねえ)を用意。

public class TxData {

    private String id;

    private int masterId;

    public String getId() {
        return id;
    }

    public int getMasterId() {
        return masterId;
    }

    public TxData(String id, int masterId) {
        this.id = id;
        this.masterId = masterId;
    }

    public static final Func1<TxData, Integer> MASTER_ID_SELECTOR = new Func1<TxData, Integer>() {
        @Override
        public Integer eval(final TxData data) {
            return data.getMasterId();
        }
    };
}
public class MasterData {

    private int id;

    private String name;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public MasterData(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static final Func1<MasterData, Integer> ID_SELECTOR = new Func1<MasterData, Integer>() {
        @Override
        public Integer eval(final MasterData data) {
            return data.getId();
        }
    };
}

あと、ヘルパーとして下記の様なTuple作成処理も用意しておいて。

public static class Functions {

    public static <T1, T2> Func2<T1, T2, Tuple2<T1, T2>> makeTuple() {
        return new Func2<T1, T2, Tuple2<T1,T2>>() {
            @Override
            public Tuple2<T1, T2> eval(final T1 value1, final T2 value2) {
                return Tuple2.pair(value1, value2);
            }
        };
    }
}

っで、テストデータとしては以下のようなものを用意するとして。

List<TxData> txs = new ArrayList<TxData>();
txs.add(new TxData("A", 1));
txs.add(new TxData("B", 2));
txs.add(new TxData("C", 2));
txs.add(new TxData("D", 4));
txs.add(new TxData("E", 4));
txs.add(new TxData("F", 4));

List<MasterData> masters = new ArrayList<MasterData>();
masters.add(new MasterData(1, "Data-1"));
masters.add(new MasterData(2, "Data-2"));
masters.add(new MasterData(3, "Data-3"));
masters.add(new MasterData(4, "Data-4"));
masters.add(new MasterData(5, "Data-5"));

まずjoin()。

for (Tuple2<TxData, MasterData> tuple : ObjectQuery.from(txs).join(masters, TxData.MASTER_ID_SELECTOR, MasterData.ID_SELECTOR, Functions.<TxData, MasterData>makeTuple())) {
    System.out.println(tuple.getValue1().getId() + ":" + tuple.getValue2().getName() );
}
[実行結果]
A:Data-1
B:Data-2
C:Data-2
D:Data-4
E:Data-4
F:Data-4

次にgroupJoin()。

for (Tuple2<MasterData, Iterable<TxData>> tuple : ObjectQuery.from(masters).groupJoin(txs, MasterData.ID_SELECTOR, TxData.MASTER_ID_SELECTOR, Functions.<MasterData, Iterable<TxData>>makeTuple())) {
    System.out.println(tuple.getValue1().getId() + ":" + ObjectQuery.from(tuple.getValue2()).count());
}
[実行結果]
1:1
2:2
3:0
4:3
5:0
変換

っで、最後はデータストア上のデータ構造とプレゼンテーション層用のデータ構造の変換するようなパターンについて。

ここではLookup(Java版はLinkedHashMapを使った簡易実装)とObjectQuery独自のrownum()を使用してchop()処理を作成するサンプル。

chopっていうのは、List<Data>を指定件数ごとに分割したList<List<Data>>みたいな形に変換する処理のこと。

rownum()は、本家LINQにおいて、いくつかのメソッドでFunc<TSource, int, TResult>を引数にした定義があるものの代わりです。

データ構造Tを、Tと連番をタプルにしたデータ構造Indexed<T>に変換する処理(Iterator)ですだ(・ω・)


まず、chop()の実装は以下のような感じで。

public static <T> Lookup<Integer, T> chop(final Iterable<T> collection, final int size) {
    return ObjectQuery.from(collection).rownum().toLookup(new Func1<Indexed<T>, Integer>() {
        @Override
        public Integer eval(final Indexed<T> param) {
            return param.getIndex() / size;
        }
    }, new Func1<Indexed<T>, T>() {
        @Override
        public T eval(final Indexed<T> param) {
            return param.getElement();
        }
    });
}

これを使って、12件のデータを5件毎に分割する処理を記述すると、以下のような感じになります。

Iterable<String> list = ObjectQuery.range(1, 12).select(new Func1<Integer, String>() {
    @Override
    public String eval(final Integer value) {
        return "Data-" + value;
    }
});

for (Grouping<Integer, String> grouping : chop(list, 5)) {
    System.out.println(grouping.getKey());
    for (String data : grouping) {
        System.out.println("  " + data);
    }
}
[実行結果]
0
  Data-1
  Data-2
  Data-3
  Data-4
  Data-5
1
  Data-6
  Data-7
  Data-8
  Data-9
  Data-10
2
  Data-11
  Data-12

RDBから取得したRowセットのデータ構造を、この種の変換でプレゼンテーション用の構造に変換すれば、jspでの記述は単純になりますよね、っという話ですだ(`・ω・´)

中身についても少し、Iterator!、Iterator!!、Iterator!!!

ObjectQueryは、LINQ(to Objects)モドキのライブラリの中でも、シンプルでベタな実装だと思います(・∀・;)

っというか、本家LINQ(to Objects)が何者か*3について、Javaプログラマにも理解してもらうことを意識して実装したものです。


ただ、Javaの言語仕様的に苦しい点もいくつかあるわけですが(・ω・;)

拡張メソッドが無い点

本家LINQ(to Objects)はIEnumerableに対する拡張メソッドとして実装されているわけで、拡張メソッドがあれば、staticなメソッドを用意するだけでFluentな記述ができますが。

LINQモドキの中には、Fluentな記述ではなくstatic importを使用して下記のような記述にしているものもあったりして。

select(where(list, predicate), selector);

一方のObjectQueryでは、Iterable<TSource>をラッピングしたしたObjectQuery<TSource>のインスタンスに変換することで、メソッドチェーンを使用した記述ができるようにしています。

そして、ObjectQuery自体がIterableをimplementsすることで、そのまま拡張forループに渡せるようにしています。

まあ、この辺は趣味の問題かしら(・ω・)?

yieldが無い点、currentとnextの違い

各種Iteratorについては、yieldが無いので状態を保存するためのメンバを用意したりとわかりにくい実装になっているものもあります。


そして以外と厄介なのが、本家におけるIEnumeratorがCurrentを処理するものなのに対して、JavaIteratorはnextを処理するものだという点。

どこら辺が厄介かと言えば、Iteratorのチェインに対して単純に次のIteratorのnextを処理すれば良いというわけではなく、Predicateでの判定結果も考慮しないといけないという所。

その為、一部IteratorのhasNext()ではプリフェッチに関連してループがより分かりにくいものになっていたり、hasNext()の複数回呼び出しを考慮して次の値をキャッシュするようになっていたりします。

この辺、自分がよく考えないで実装しているだけで、もっと綺麗に書けるかも(´・ω・`)

ラムダが無い点、Genericsの扱い、functorについて

ラムダが無い点について、利用者から見た場合のfunctorを匿名クラスで記述するので冗長になる点については、サンプルにあるようなFunctionsクラスを用意したり、selectorをstaticメンバとして保持することで若干の緩和は出来ますが(・∀・;)


ObjectQuery実装内の話については、Genericsの扱いの違いから、functorクラスの使用方法が本家とは違う考えになっているものがあります。

例えば、本家ではPredicate<TSource>ではなくFunc<TSource, bool>な点については、JavaだとBooleanにしかできないので、nullが返されないようにFuncではなく別途Predicateを用意していたりとか(・ω・)

Func<TSource, int, TResult>を引数に取るメソッドの代わりにrownum()を用意しているのも似たような理由だったりして。



っということで、自作ライブラリ(実用というより学習用)の紹介日記でした(・ω・)

*1:ちゃんとしたのをお探しなら、Quaereとかlambdajとかをドゾ(・∀・)

*2:逆に、それ以外の用途ではあまり使用しないかも(・ω・)?

*3Iterator!、Iterator!!、Iterator!!!のチェーン(・∀・)

2011-10-30

[]SI屋的(?)JavaScript(jQuery)活用方法

SI屋的なWebシステムの開発において、どこまでJavaScriptを使うのかについては、いくつかパターンがあると思うわけですが。

1. 機能要件では無いし、お金にならないのでいっさい使わないパターン(´д`;)

2. jQueryとdata属性なんかを使えば、宣言的にそれなりに装飾されたものをコストをかけずに作れるので、そのパターン(・ω・)

3. フルJavaScript…っというか、MVVMライブラリを使うだけで無く、GWTみたいなものを使わないとやる気が起きないパターン(・∀・;)

っで、未だに1のパターンも多かったりする一方で、jQueryの登場以降、使い方を考慮することで、SI屋的なシステムでもコストをかけずにUIをリッチにすることも可能になってきたので、その方法について書きたいと思います(`・ω・´)

そもそも

SI屋的なシステム開発における統制の観点から言うと、JavaScriptの記述は他のロジック記述に比べて個人差が出るので*1、担当者個々に処理を書かせたくないという話があるかと思います(´д`;)

また、各html(jsp、aspx)ページ内にJavaScriptの記述が混在すると、メンテナンス的に好ましくないという懸念もあるかと思います。


今回提唱する方法は、上記懸念に対する一定の回答を示しながら、JavaScriptを活用してUIをそれなりにリッチにする方法だと思います(・ω・)

その方法とは、簡単に言えば「data属性の活用と宣言的な記述」です。


なお、以降はjQuery前提での話となります。

data属性

data属性とはなんぞや?、っという人にはググって貰うとして(・∀・;)

要は、data属性を用いて、html内には処理(JavaScript)に必要なパラメータのみを記述することで、htmlからJavaScriptの完全分離をしようというのが今回提唱する案の肝になります。


簡単な例として言えば、<button>が押されたときに画面遷移したい、っというような要求に対して、

<button type="button" onclick="location.href='/home'">初期値に戻す</button>

の様に処理も含めて記述するのでは無く、

<button type="button" class="link" data-link="/home">初期値に戻す</button>

のように、宣言的な記述にして、処理(JavaScript)をhtmlから分離しよううという話です(・ω・)

宣言的記述

動作を書くのでは無く、それが何をするものであり、必要なパラメータは○○であるということをに記述することで、処理(JavaScript)とhtmlを分離することが出来ます。

その場合、処理(JavaScript)の方はどう書くのかという話になりますが、まず、基本の考え方としては以下の様になります。

…っと言ってもよく分からない(´д`;)と思うので、前述の<button>による画面遷移を例にすれば、JavaScriptとしては以下の様な処理を用意します。

$(document).ready(function() {
    $(".link").live("click", function() {
        window.location = $(this).data("link");
        return false;
    });
});

これをどう捕らえるかと言えば、[class=link]という「コンテキスト」に対して処理を適用する、っという風に読んでください。

そして、必要なパラメータ(この例で言えばリンク先の情報:data-link)も宣言的に記述されているので、それを参照して使用する、っという形になります。


このパターンを使用すれば(適用コンテキスト設計は必要ですが)、htmlJavaScriptの完全分離が出来るようになります(`・ω・´)


また、この様な処理については、jQueryや各種プラグイン、及び他のライブラリとは別に、アプリケーション固有のコンテキスト適用層として分離することで、汎用性と柔軟性のバランスを取ることができると考えます。

html上では下記の様にインクルードするイメージで。

...


<script src="/js/jquery/jquery-1.5.2.min.js" type="text/javascript"></script>
<script src="/js/jquery/jquery-ui-1.8.13.min.js" type="text/javascript"></script>
...その他jQueryのプラグイン、ここまでは共通部品


<script src="/js/app/common.js" type="text/javascript"></script>
<script src="/js/app/link.js" type="text/javascript"></script>
<script src="/js/app/partial.js" type="text/javascript"></script>
...アプリケーション固有のコンテキスト適用層、共通部品をどう使うか?、っとい層

...

サーバアプリケーションアーキテクチャを例にして見れば、汎用FWが提供する機能とは別に、そのアプリケーション固有のレイヤスーパータイプを用意するような感覚に近い感じでしょうか(・ω・)?

処理パターン

コンテキスト適用層は処理毎に分離して、画面毎に必要なものをインクルードする形でメンテナンスしやすくすることが出来ます。

っということで、以下、処理パターン毎に分離する時の例をいくつか書いてみます。

非同期ロード(asyncload.js)

ポータル的な画面などで複数のWidget的な要素を配置して、それを非同期にロードして見せたいような場合の処理の例です。

$(document).ready(function() {
    $(".asyncload").each(function(index, e) {
        var $this = $(e);
        $.ajax({
            type: "GET",
            url: $this.data("url"),
            context: $this,
            success: function(data, textStatus, xhr) {
                $(this).empty().append(data);
            },
            error: function() {
            },
            complete: function() {
            },
            dataType: "html"
        });
    });
});

[class=asyncload]のコンテキストに対して、data-urlから表示する先のURLを取得し、それのhtmlを非同期に取得して画面を更新する、っという処理です。

この場合の適用コンテキストの例としては、下記の様になります。

<div id="weatherWidget" class="asyncload" data-url="/widget/weather">
<img src="/images/loading.gif"/>
</div>

画面の実装者は上記の様なhtmlを記述するだけでよくなり、htmlJavaScriptの分離、もとい、個々の画面担当者とJavaScript担当者という形での作業の分離が(SI屋的に)出来るようになります(・∀・;)

パーシャルアップデート(partial.js)

もう少し複雑なパターンとして、部分更新(ASP.NETにおけるUpdatePanel的なもの)の処理例を書いてみます。

$(document).ready(function() {
    var partialUpdate = function(e, name) {
        var form = e.parents("form");

        var url = e.data("url");
        if (!url) {
            url = form.attr("action");
        }

        var target = e.data("target");
        if (!target) {
            target = form.data("target");
        }

        var data = form.serializeArray();
        data.push({ name: "_partial", value: name });

        $.ajax({
            type: "POST",
            url: url,
            data: data,
            context: target,
            success: function(data, textStatus, xhr) {
                $(this).empty().append(data);
            },
            error: function() {
            },
            complete: function() {
            },
            dataType: "html"
        });
    };

    $("select.partial").live("change", function() {
        var $this = $(this);
        partialUpdate($this, $this.attr("name"));
    });

    $("input[type='button'].partial").live("click", function() {
        var $this = $(this);
        partialUpdate($this, $this.attr("name"));
    });

    $("a.partial").live("click", function() {
        var $this = $(this);
        var name = $this.attr("href")
        if (name.indexOf("#") == 0) {
            name = name.slice(1);
        }
        partialUpdate($this, name);
        return false;
    });
});

解説の前にhtmlも。

<div id="updatePanel">

<form action="/partial" method="POST" data-target="#updatePanel">

<input type="text" name="data1" value=""/>

<select name="data2" class="partial">
<option value="1">データ1</option>
<option value="2">データ2</option>
<option value="3">データ3</option>
</select>

<input type="button" name="trigger" value="トリガー" class="partial"/>

<a href="#link" class="partial">パーシャルリンク</a>

<input type="submit" value="更新"/>

</form>

</div>

この例は、[class=partial]なselect、button、リンクに対する操作に対して、formのaction先にAjaxでデータをPOSTする(応答では

内を返して貰う)ことにより、画面全体の更新無しで、同じ画面(from)の値を書き換えながら処理を行うという、ASP.NETにおけるUpdatePanel的な処理を実現するものです。

サーバ側のハンドラでは、_partialパラメータがdata2、trigger、link及び無しの何れかを判別することで、どの要素による部分更新なのか、あるいはsubmit処理なのかを判別できます。


画面担当者は、上記のルールに従って実装すれば、後は既存の作りと変わらずにAjax的なformを作れるようになる、っと言うわけです(・ω・)

まとめ

data属性と宣言的な記述により、htmlJavaScriptの完全分離が出来るようになりました(・∀・)

また、このことによりJavaScriptの実装は少数のコンテキスト適用層を作る人に任せることが出来るようになりました。


っということで、SI屋的な開発でも、コストをあまりかけず、品質面での統制も問題無く、Ajax/JavaScriptを使ったそれなりにリッチなWebシステム開発が出来るようになりますね、っという話ですが、どうでしょう(・ω・)?

おまけ

自分の出自はWindowsプログラマ〜バックエンド/サーバ側なので、JavaScript自体それほど好きというわけでも無いですが、なんだかんだ言いつつ、去年はJSのコードを結構書いていたたり、本も読んだりしたので、これも時代でしょうかね(・ω・)?

ちなみにこれ、jQuery in Actionの邦訳ね。*2


っで、SI屋的なWebシステム開発におけるJavaScriptの課題としては、自分としては以下のようなものが残っていると思っています。

*1:正しくは、Controller/ServiceやSQLコピペバインダー的な作業よりも、UI制御の記述の方が頭を使う、という意味だと思いますが(・∀・;)

*2:余談だけど、元がin Actionシリーズの邦訳だっていうのが分かりにくい書籍が最近多いよね(・∀・;)

*3:単純にMVxパターンのJS版というだけでなく、サーバコードからの自動生成/自動連携的なものも欲しい、っというのは贅沢ですか(・∀・)?

2011-05-03

[][]CentOS上でMonoを使ってRazorを動かす

最近はCentOS(RHEL)でもMonoのリポジトリを設定すれば、yumで最新版をインスコできるとか、楽に環境設定できるようになっているので、その手順メモ(・∀・)

とりあえずCentOSが用意できているところからはじめます。*1

Monoのインストール

まずはMonoのリポジトリを追加(・ω・)

> vi /etc/yum.repos.d/mono.repo

内容は下記で。

[mono]
name = novell-mono
baseurl=http://ftp.novell.com/pub/mono/download-stable/RHEL_5/
enabled=0
gpgcheck=0

ついでMonoのインスコですが、とりあえずこのあたりを入れておけば事足りる(・ω・)?

yum --enablerepo=mono install mono-addon-core mono-addon-data mono-addon-web mono-addon-devel mono-addon-winforms mono-addon-wcf mono-addon-libgdiplus0 mono-addon-extras mod_mono-addon

これだけでインスコ完了、簡単ですね(・∀・)


バージョンの確認しておきましょうか。

> /opt/novell/mono/bin/mono -V
Mono JIT compiler version 2.10.2 (tarball Mon Apr 18 18:57:39 UTC 2011)
Copyright (C) 2002-2011 Novell, Inc and Contributors. www.mono-project.com
        TLS:           __thread
        SIGSEGV:       altstack
        Notifications: epoll
        Architecture:  x86
        Disabled:      none
        Misc:          debugger softdebug
        LLVM:          supported, not enabled.
        GC:            Included Boehm (with typed GC and Parallel Mark)

RazorなWebアプリを動かす

まず、アプリケーション用のディレクトリを作成(・ω・)

> mkdir /var/www/sample

ついでApacheの設定ですが、mod_monoの設定自体は/etc/httpd/conf.d/mod_mono.confに設定済みになっているので、アプリケーションの設定だけを追加します。

> vi /etc/httpd/conf/httpd.conf

最後に下記の内容を追加。

Alias /sample/ "/var/www/sample"

AddMonoApplications sample "/sample:/var/www/sample"

MonoServerPath sample "/opt/novell/mono/bin//mod-mono-server4"
MonoSetEnv sample MONO_IOMAP=all

<Location "/sample">
    MonoSetServerAlias sample
    SetHandler mono
</Location>

環境設定はこんなんでOK。


次はアプリケーションですが、これはVisual Studio 2010で作成することにして。

ASP.NET MVC 3 Web アプリケーションを作成して、ビューエンジンにRazorを選択。

参照設定に、いかのうち足りないものを追加し、プロパティでローカルコピーをTrueに設定します。

  • System.Web.Helpers.dll
  • System.Web.Mvc.dll
  • System.Web.Razor.dll
  • System.Web.WebPages.Deployment.dll
  • System.Web.WebPages.dll
  • System.Web.WebPages.Razor.dll

あとは、プロジェクトをビルドしたものをCentOS上の/var/www/sampleにデプロイすれば動作確認できますよヽ(・∀・)ノ


ヘッダも確認してみるとこんな感じになっていて。

HTTP/1.1 200 OK
Date: Tue, 03 May 2011 08:17:02 GMT
Server: Apache/2.2.3 (CentOS)
X-AspNetMvc-Version: 3.0
X-AspNet-Version: 4.0.30319
Content-Length: 1215
Cache-Control: private
X-Powered-By: Mono
Connection: close
Content-Type: text/html; charset=utf-8

ApacheとMonoで動作していることが確認できました。


しかし、これだけ簡単に環境設定できるようになってくると、なにか実戦投入したくなりますね(・∀・;)

*1:自分は5.6 x64上で確認しました。

2011-03-31

[]Virtual PCでWindows Embedded Compact 7を動かしてみる

MSDNサブスクライバからWindows Embedded Compact 7が落とせるようになっていたことには気がついていたので(・∀・)

自分も、Platform BuilderでビルドしたWindows Embedded Compact 7のイメージをVirtual PCを使って動かしてみたのでした。

f:id:machi_pon:20110401205728p:image

自分もやってみたいという人は、下記のURLを参考にどぞ(・ω・)つ

概要

詳細はリンク先を見ればわかるので、やることの概要だけ書いておくとこんな感じです。

  • Visual Studio 2008 + SP1を用意
  • MSDNサブスクライバからWEC7をダウンロードしてインスコ
  • Virtual PC用のOSイメージ(nk.bin)をビルド
  • WEC7中に用意されているVirtual PCのイメージを起動
  • 仮想マシンはDHCPでIPアドレス取得、TFTPでイメージを取得
  • OSイメージ起動

っという流れ。


後は、自分がはまったポイントについても書いておきます。

DHCPサーバを忘れない

仮想マシンはDHCPでIPアドレスを取得して動作するので、DHCPサーバを忘れないように。

自分の場合、DHCPサーバはいたんだけど、リソースが全て払い出し済みだったというオチなんだけど…(´д`;)

TFTPでのイメージダウンロードに失敗する

仮想マシンがDHCPでIPアドレスを取得し、TFTPでイメージを取得しにいった後に次のようなメッセージが表示されてしまい(・ω・;)

ERROR: BootTransportPb: TFTP timeout occured!

ちょっと調べたら、UDPのチェックサムを無効にしたら良いんじゃよ、っという記述があったのでやってみたら解消しました。

↓みたいな、ネットワークドライバの設定の話ね。

f:id:machi_pon:20110401205830p:image

さて、個人的にはWP7よりも、WEC7ベースの業務用ハンディが気になっていたりもするわけですが(・∀・;)

ただまあ、昨今のAndroid端末なんかも見ていると、アプリケーションプラットフォーム的な意味でのWECは微妙かな〜と思わなくもなかったり(・ω・;)

カーネル的な意味でのWECは好きだし、本業の組込屋さんにとってはそれが標準でしょうけど、アプリケーションプラットフォームとして考えた場合、やっぱりちょっと「素」すぎるんですよね。

各種サービスを抽象化してモダンなオブジェクトモデルを用意したり、用途毎のプロファイルフレームワークみたいなものが強化されないと、上位層よりのアプリを作る人としては物足りないかな(・ω・)?、とか。


量販店のバックヤードとか物流の現場で使っているようなハンディーターミナルについては、現状Windows CE端末意外にHW的にも適合するものが無いし、しばらくは置き換わらないでしょうが。

でも、検針端末とかセールス端末みたいなものについては、Android端末もありかな〜という気にはなっていて。


UI的な表現力や作りやすさは十分にあるし、データアクセスや通信についても事足りているし。

一方で、デバイス関連、ラインプリンタ制御とかバーコードリーダーのHW的特性(単にカメラで読めれば良いと言うことでは無くて、接写できるタイプかどうかとか)みたいな所は不十分なので、バックヤードにはまだ無いと思っていますけど。

あと、アプリケーションアーキテクチャ的な部分について言えば、画面遷移、DI、バインディング、ヴァリデーションとかもイメージはできつつあるので(`・ω・´)

そういったインフラストラクチャ層を用意して、APTを使った自動生成なんかの仕組みも用意すれば、かなり良い感じでAndroidでも業務アプリが作れるよね〜、つって(・∀・)


〜とか、3月は主にAndroidで遊んでいた*1人は思うのでした(・ω・)

*1:といっても、半分以上NDKを弄っていたんだけど(・ω・)

2010-11-30

[]Mono 2.8上でSmart.Web.Mobileを使ってみる

最近、ちまちまとMonoを試していたのでメモ。


Monoも、Linux上でyumやapt-get一発で最新版がインストールされるようになると、もう少し弄ってみる気になるんですが(・ω・)

手動インストールも一つや二つなら良いんですが、依存関係が多くなってくると面倒で(´・ω・`)

…などと思いつつWebをブラブラしていたら、インストール用のスクリプト*1を作っている人が居たので、MonoでASP.NET 4.0する環境を自分も作ってみました。

元情報はこちら。

Setting up Mono 2.8 with Asp.Net 4.0 and MVC2 on Ubuntu with MySql Membership

スクリプトはUbuntu用とFedora用があるみたいですが、今回はUbuntuで試してみました。*2

Ubuntu

まずはUbuntuの用意から(・ω・)

自分はVMwareを使ってVM上に環境を構築。

Ubuntu Server 10.04をダウンロードしてきてインストール。

以下、Ubuntu Serverのインストールについてざっくりと(・ω・)

  • VMはUbuntu Linux (64-bit)で作成
  • インストール開始、Languageは日本語
  • キーボードは適当なのを選択、なんとなくMicrosoft Natural Wireless Ergonomic Keyboard 7000とかで、レイアウトはJapanで
  • ネットワークはDHCPで入れて後で固定に変更
  • パーティションとかはデフォルトでディスクに書き込み
  • ユーザー名、パスワードを入れて、ホームディレクトリの暗号化はしない
  • プロキシを入力、自動アップデートは無しで、ソフトウエアの選択ではOpenSSH serverのみを選択しておく
  • ブートローダーはデフォルト

これでインストールは完了(・ω・)

ついで初期設定。

とりあえずIPアドレスを固定に変更。

sudo vi /etc/network/interfaces
auto eth0
#iface eth0 inet dhcp
iface eth0 inet static
address x.x.x.x
netmask x.x.x.x
gateway x.x.x.x
sudo /etc/init.d/networking restart

ついでソフトウエアを最新版に更新。

sudo apt-get update
sudo apt-get upgrade

VMware Toolsのインストールについては省略。

これでOSの準備はOK、次はMonoのインスコ(・ω・)

Mono

GitHubからインストールスクリプトを取得して実行。

wget --no-check-certificate https://github.com/nathanb/iws-snippets/raw/master/mono-install-scripts/ubuntu/install_mono-2.8.sh
chmod +x install_mono-2.8.sh
./install_mono-2.8.sh

コンパイルに時間がかかるのでしばらくおやつタイム(・∀・)

コンパイルが終わったら、/etc/environmentにパスを追加。

sudo vi /etc/environment
PATH="/opt/mono-2.8/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"

GUI周りをインストールするとMono 2.4が入るようですが、自分で入れた2.8を優先するように設定。

っで、設定を反映させた後で実行結果の確認。

machi@vm-mono-ubuntu:/srv/www$ mono -V
Mono JIT compiler version 2.8 (tarball 2010121日 水曜日 18:19:26 JST)
Copyright (C) 2002-2010 Novell, Inc and Contributors. www.mono-project.com
        TLS:           __thread
        SIGSEGV:       altstack
        Notifications: epoll
        Architecture:  amd64
        Disabled:      none
        Misc:          debugger softdebug
        LLVM:          supported, not enabled.
        GC:            Included Boehm (with typed GC and Parallel Mark)

MonoのインストールまではこれでOK。

後はApacheでmod_monoを有効にするための設定を。

cd /etc/apache2
sudo mv mod_mono.conf mods-avail*
sudo ln -s /etc/apache2/mods-available/mod_mono.conf /etc/apache2/mods-enabled/mono.conf
sudo service apache2 restart

Ubuntuだと設定ファイルが細かく管理されてんのね(・ω・)

Webアプリ

次にアプリケーションの設定を。

今回はSmart.Web.Mobileのサンプルと、それとは別に新規作成したWebアプリケーションの2つを、別アプリケーションプールに配置するような設定にしてみます(・ω・)

Smart.Web.Mobileのサンプルと、新規に作成したWebアプリケーションをそれぞれVisual Studio 2010でビルドしておきます。


っで、サーバ上の設定については、/srv/wwwディレクトリ下を各アプリ用の仮想ディレクトリとして使用する事にします。

まずはディレクトリを用意して。

sudo mkdir /srv/www

次に、この下にビルドしたアプリケーションを配置します。

今回はSmart.Web.Mobileのサンプルをmobileディレクトリに、新規に作成したWebアプリケーションをsampleディレクトリに配置します。

っで、配置が終わったら権限をば設定。

sudo chown root:www-data /srv/www -R
sudo chmod 775 /srv/www -R

こんな状態になっていればよかとですたい(・ω・)

machi@vm-mono-ubuntu:/srv/www$ ls -al
合計 24
drwxr-xr-x  6 root www-data 4096 2010-12-02 18:57 .
drwxr-xr-x  4 root root     4096 2010-12-02 18:24 ..
drwxr-xr-x 16 root www-data 4096 2010-12-02 17:03 mobile
drwxr-xr-x 11 root www-data 4096 2010-12-02 18:56 sample

machi@vm-mono-ubuntu:/srv/www$ ls -al mobile/
合計 92
drwxr-xr-x 16 root www-data 4096 2010-12-02 14:03 .
drwxr-xr-x  6 root www-data 4096 2010-12-02 13:57 ..
drwxr-xr-x  2 root www-data 4096 2010-12-02 18:49 App_Browsers
drwxr-xr-x  3 root www-data 4096 2010-12-02 18:49 App_Data
drwxr-xr-x  2 root www-data 4096 2010-12-02 18:49 Content
...

そして仮想ディレクトリの設定を行います。

sudo vi /etc/apache2/sites-available/mono.conf

内容についてはこんな感じで。

Alias /mobile/ "/srv/www/mobile"
Alias /sample/ "/srv/www/sample"
#Alias /sample2/ "/srv/www/sample2"

AddMonoApplications mobile "/mobile:/srv/www/mobile"
AddMonoApplications sample "/sample:/srv/www/sample"
AddMonoApplications sample "/sample:/srv/www/sample2"

MonoServerPath mobile "/opt/mono-2.8/bin/mod-mono-server4"
#MonoDebug mobile true
MonoSetEnv mobile MONO_IOMAP=all

MonoServerPath sample "/opt/mono-2.8/bin/mod-mono-server4"
#MonoDebug sample true
MonoSetEnv sample MONO_IOMAP=all

<Location "/mobile">
    MonoSetServerAlias mobile
    SetHandler mono
</Location>

<Location "/sample">
    MonoSetServerAlias sample
    SetHandler mono
</Location>

#<Location "/sample2">
#    MonoSetServerAlias sample
#    SetHandler mono
#</Location>

mobileとsampleの2つのアプリケーションプールを作って、Smart.Web.Mobileのサンプルと新規作成したサンプルをそれぞれに設定する感じ(・ω・)

っで、sites-enabledの方にもリンクを張って再起動。

sudo ln -s /etc/apache2/sites-available/mono.conf /etc/apache2/sites-enabled/000-mono.conf
sudo service apache2 restart

これで、http://server/mobile/アクセスするとSmart.Web.Mobileのサンプルが、http://server/sample/アクセスすると新規作成したサンプルにアクセスできることが確認できます(・∀・)

プロセスもこんな感じでMonoが動いている事を確認できます。

machi@vm-mono-ubuntu:~$ ps -aux | grep mono
www-data  1242  0.5  5.3 197356 55008 ?        Ssl  19:39   0:02 /opt/mono-2.8/bin/mono **debug /opt/mono-2.8/lib/mono/4.0/mod-mono-server4.exe **filename /tmp/mod_mono_server_mobile **applications /mobile:/srv/www/mobile **nonstop
www-data  1244  0.3  3.2 173100 33268 ?        Ssl  19:39   0:01 /opt/mono-2.8/bin/mono **debug /opt/mono-2.8/lib/mono/4.0/mod-mono-server4.exe **filename /tmp/mod_mono_server_sample **applications /sample:/srv/www/sample **nonstop

動作確認

ssbでの表示結果ですが、レスポンスヘッダの所を参照(・∀・)

f:id:machi_pon:20101202161236p:image

まとめ

っで、これでLinuxサーバ上でMonoを使い、Smart.Web.Mobileを使ってガラケー向けサイトも作れるようになった(?)わけですが…。

ぶっちゃけ、公開サービスで.NETしたかったら、実際にはクラウドなりなんなり使えば良いと思うので、こんなことしないと思うんだけどね(・∀・;)

むしろ、イントラとかでLinux上に載らなければいけないケースで、でもWebアプリは.NETで作りたいな〜なんて時にはありかもしれない(`・ω・´)

色々動かしてみた感じ、思った以上に問題なく動く感じだし(・∀・)


今回はMonoDevelopは入れていないので、Visual Studio 2010上でビルドしたものをそのままサーバにコピーして実行しましたが。

出来れば、サーバ上で自動ビルド、自動テスト、自動デプロイまでの環境を構築したいところですね(・∀・)

*1:やっていることはダウンロード、makeのバッチ実行だけど。

*2:普段はCentOSを使っている自分なのでFedoraの方が填らなかったのかも(´д`;)

2010-10-30

[]Silverlightでカスタムページングをしたい時

また日記を放置していたので、月一くらいでは小ネタも書いておこうかと(・∀・;)

元ネタはこちらですが、ちょっと使ったのでSilverlightでのカスタムページングについて。

http://weblogs.asp.net/manishdalal/archive/2009/10/01/silverlight-3-custom-sorting-with-paging-support.aspx


SilverlightでDataGrid、DataPagerを使ってデータ管理アプリケーションを作るときの話ですが(・ω・)

Silverlightで作ったクライアントにデータを提供するサービス側も.NETで作って、WCF RIA ServicesでLinqToEntitiesDomainServiceなんかを使えば、ページング処理なんかも楽ちんぽんと作れて、ちゃんとSkip() & Take()した必要なデータだけの取得なんかも出来ちゃうわけですが。

でも、サービス側がJava製だったり既存のRESTなサービスを使いたいときとか、パラメータを自分で組み立ててデータ取得の通信を行いたいときにはどうするか(・ω・)?

もちろん、ページに必要なデータのみをオンデマンドで取得する方法で。*1


っで、そこで登場するのが、ICollectionView、IPagedCollectionViewですね(・∀・)

ICollectionViewの定義はこんなんですが。

public interface ICollectionView : IEnumerable, INotifyCollectionChanged
{
    event EventHandler CurrentChanged;
    event CurrentChangingEventHandler CurrentChanging;

    bool Contains(object item);
    IDisposable DeferRefresh();
    bool MoveCurrentTo(object item);
    bool MoveCurrentToFirst();
    bool MoveCurrentToLast();
    bool MoveCurrentToNext();
    bool MoveCurrentToPosition(int position);
    bool MoveCurrentToPrevious();
    void Refresh();

    bool CanFilter { get; }
    bool CanGroup { get; }
    bool CanSort { get; }
    CultureInfo Culture { get; set; }
    object CurrentItem { get; }
    int CurrentPosition { get; }
    Predicate<object> Filter { get; set; }
    ObservableCollection<GroupDescription> GroupDescriptions { get; }
    ReadOnlyObservableCollection<object> Groups { get; }
    bool IsCurrentAfterLast { get; }
    bool IsCurrentBeforeFirst { get; }
    bool IsEmpty { get; }
    SortDescriptionCollection SortDescriptions { get; }
    IEnumerable SourceCollection { get; }
}

カスタムページングに関連するのは、CanSort、SortDescriptions、Refresh()/DeferRefresh()のあたり(・ω・)

ICollectionViewの実装をDataGridにバインドしてソートをすると、DeferRefresh()が呼び出されます。

DeferRefresh()は更新遅延用のメカニズムなので、IDisposable.Dispose()内でRefresh()メソッドをコールバックするオブジェクトを返してあげればOK。

っで、Refresh()内ではイベントをあげて、ソート条件にあったデータの取得処理を呼び出し、コレクションを再構築してあげるっと。

ソートで使用する列や昇順降順はSortDescriptionsプロパティを参照、っと。


っで、ICollectionViewの実装としてはObservableCollectionを継承したクラスを作る方向で(・ω・)

CurrentItem、CurrentPositionに対応するための現在位置のプロパティを用意して、コレクションの操作時にはその情報も更新して。

CanFilter、CanGroupのあたりは、とりあえずソートに対応するだけであればfalseを返す実装でOK。

あとは、MoveXxx()を実装して、CurrentChanged、CurrentChanging及びOnPropertyChanged()を適切に呼び出してあげれば、オンデマンドでのデータ取得に対応したコレクションの完成っと。


更にページングに対応するためには、IPagedCollectionViewも実装(・ω・)

IPagedCollectionViewの定義はこんなんですが。

public interface IPagedCollectionView
{
    event EventHandler<EventArgs> PageChanged;
    event EventHandler<PageChangingEventArgs> PageChanging;

    bool MoveToFirstPage();
    bool MoveToLastPage();
    bool MoveToNextPage();
    bool MoveToPage(int pageIndex);
    bool MoveToPreviousPage();

    bool CanChangePage { get; }
    bool IsPageChanging { get; }
    int ItemCount { get; }
    int PageIndex { get; }
    int PageSize { get; set; }
    int TotalItemCount { get; }
}

IPagedCollectionViewの実装をDataPagerにバインドすると、ページの変更時にはMoveToXxx()が呼び出されるので(・ω・)

ICollectionViewの実装にIPagedCollectionViewも実装してあげて、MoveToXxx()の中でRefresh()を呼び出すようにすれば、ページングもオンデマンドで行えるようになるっと。


っで、リンク先からはその実装例がダウンロードできますね(・∀・)

ICollectionView及びIPagedCollectionView実装のPagedSortableCollectionViewの使い方はこんなんです。


まずViewModelをこんな感じで用意して。

public class HogeViewModel
{
    public PagedSortableCollectionView<Hoge> DataList { get; private set; }

    public int PageSize
    {
        get { return DataList.PageSize; }
    }

    public HogeViewModel()
    {
        DataList = new PagedSortableCollectionView<Hoge>();
        DataList.OnRefresh += new EventHandler<RefreshEventArgs>(DataList_OnRefresh);
        GetData();  // 初期データ取得
    }

    // DataGrid、DataPagerでの操作からこのハンドラが呼び出される
    private void DataList_OnRefresh(object sender, RefreshEventArgs e)
    {
        GetData();
    }

    private void GetData()
    {
        // 下記の条件を元に検索
        int take = DataList.PageSize;
        int skip = DataList.PageIndex * DataList.PageSize;
        foreach(SortDescription sortDesc in DataList.SortDescriptions) { ...  }

        // 本当はここで非同期通信開始
...
        // 完了ハンドラでデータ更新
        DataList.Clear();
        foreach (Hoge hoge in list) {
            DataList.Add(hoge);
        }
        DataList.TotalItemCount = count;
    }
}

DataGrid、DataPagerとはこんな風にバインディング

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.DataContext>
        <vm:PeopleViewModel />
    </Grid.DataContext>
    <data:DataGrid x:Name="dataGrid"
                   AutoGenerateColumns="True"
                   Grid.Row="0"
                   ItemsSource="{Binding DataList}" />
    <data:DataPager x:Name="dataPager"
                    Grid.Row="1"
                    PageSize="{Binding PageSize}"
                    DisplayMode="FirstLastPreviousNext"
                    IsTotalItemCountFixed="True"
                    Source="{Binding DataList}" />
</Grid>

これで、カスタムソート/ページングしたデータの取得をオンデマンドで行う仕組みができました(・∀・)

将来のWCFはRESTfulということですが、このあたりのサポートも強化されると嬉しいかな〜(・ω・)

*1:サンプルレベルのものだと、画面初期表示に必要なデータを取得するようなものしかなかったりしてあれですが(´д`;)

2010-09-23

[][][]仕事の成果を流用して趣味的な監視アプリケーションを作成メモ

放置していたけど、たまには雑記を書いてみたり(・∀・;)

最近、vSphere SDKを叩いたり、運用監視系のツールを作ったりもしていたので、その成果を流用して個人的に作った監視アプリケーションについて書いてみます。

システム概要

さて、開発や保守用途でVMを使うのは一般的になりつつある今日この頃ですが(・∀・)

VMの台数が増えてくると、稼働監視やリソースの使用状況のチェックも必要になってきます(・ω・;)


プライベートクラウドとか、会社のちゃんとした環境であれば、お金を出して稼働監視ツールを導入したりしますが。

自分達で勝手に作っている開発環境であれば、VMホストも無料のESXiだったり、監視ツールもOSSのものを使用したりと言うことが多いと思います(・ω・)


っで、稼働監視の方法についてですが、VMゲストのWindowsやLinuxについては、稼働監視ツールのエージェントをインストールして、情報収集を行うかと思います。

しかし、ESXiのホストについては、エージェントが対応していないケースがほとんどだと思います。

Linuxベースではあるので、staticな感じで無理矢理コンパイルしてみたり、スクリプトベースのエージェントもどきを作ってみるという手も無くはないですが(´д`;)

でも、そういった小細工はしなくても、ESXiが公開するWebサービスから各種パフォーマンスカウンターの値(VMware vSphere Clientのパフォーマンスタブで表示できる値に同じ)を取得できるので、その値を使用した稼働監視なら行えます(・∀・)


また、パフォーマンスカウンターについてはゲストの値も取得できるので*1、これらの値を収集してWebで表示する簡易監視アプリケーションを作ってみました(・∀・)

システム構成

概略図はこんなんです(・ω・)

f:id:machi_pon:20100923220806p:image

ESXiが公開するWebサービスから、パフォーマンスカウンタを定期的に取得するアプリをJavaで作成して。

このアプリを、Linux上でデーモンとして動作させて、取得した各種値はMySQLに保存しています。


また、MySQLに保存した値から、稼働情報のダッシュボード表示と、パフォーマンス値のグラフ表示を行うアプリケーションASP.NET MVCで作成して、Windows上で稼働させています(・ω・)


…っで、なんかJavaも.NETも使ったり、Linux上でもWindows上でもアプリが動いていたりしますが、本質的にはこんな構成にする必要はありません(´д`;)

このシステムと同様の事はJava/Linuxオンリー、あるいは.NET/Windowsオンリーな構成でも構築可能です。


単に、仕事でやったことの成果を流用したり、開発環境に相乗りさせているため、こんな構成になっているだけです(・∀・;)

自分は最近、JavaでVMwareのWebサービスを叩いたり、.NETでグラフ表示ということをやっていたので、その成果をそのまま流用するため、こんな構成になっているのでした(´д`;)

Webアプリケーション

っで、作成したアプリケーションですが、ホームを開くと下記の様なダッシュボードが表示されます。

f:id:machi_pon:20100923220912p:image

これを見せた人からは、EVAっぽいとか、カッコイイwwwとか笑われましたが、まあ、ゼロ年代的なアニメやゲームっぽいものは意識しました(・ω・)

Webベースで単純に作って、Ajaxによる定期更新をしているだけなので、それっぽく見えるのは第一印象だけだったりしますが(´д`;)

本当は、状態が変わるときにアニメーションしたり、状態が一斉に警告になった時に挿入歌*2が流れたりすると、バカっぽくてもっとカッコイイ感じになりますが(^Д^)


っで、表示内容についてですが、各箱がVMホスト/ゲスト(最上段がホスト、その下がそこにぶらさがっているゲスト)を表していて、状態によって表示される文字や色が変わります。

状態としては稼働(RUNNING)、注意(WARNING)、警告(ALERT)、停止(STOPPED)、消失(LOST)という感じで。

ちなみに、このアプリでチェックしているのはCPU使用率、メモリ使用率、ディスク使用率だけですが、チェックする項目や閾値は設定で変更できるようにしています。


そして各箱をクリックすると表示されるのが、以下のグラフになります。

f:id:machi_pon:20100923220943p:image

このグラフの値は、VMwareのWebサービスを使ってパフォーマンスカウンタから取得した物ですが、ディスク使用量や稼働VM数などの情報については、インベントリ情報などから計算した値です。

この辺の値の計算も、プラガブルな実装になっていて、計算式クラスを増やせば任意の項目を追加できるような仕組みにはしています(`・ω・´)

仕事の成果の流用:パフォーマンス収集

っで、この監視アプリを作るために流用した、仕事の成果についても少し。


最近、VMwareのWebサービスを使ってパフォーマンス収集みたいな事を仕事でやっていたんですが。

この辺、情報が少なくて(特に日本語の情報は皆無で)ちょっと苦労しました(´д`;)

公式のProgramming Guideに一通りの事はのっているんですが、個人的にはちょっとわかりにくい感じがして(´・ω・`)

もっとサンプルソースも欲しいんだけどということで、結局この書籍を購入したのでした。

VMware VI and vSphere SDK: Managing the VMware Infrastructure and vSphere

VMware VI and vSphere SDK: Managing the VMware Infrastructure and vSphere

もっとも、書籍が届く前になんとかなっちゃったんですけどね(´・ω・`)


ちなみに、稼働情報の収集に使用しているのはVimPortType.retrieveProperties()とVimPortType.queryPerf()の2つです。

まずは、retrieveProperties()でHostSystem、VirtualMachine、Datastoreの情報を収集して。

その後、ホストと各VMについては、queryPerf()により各種パフォーマンスカウンタ値を取得して。

パフォーマンスカウンタから直接取得できないような値については、取得したインベントリ情報とパフォーマンスカウンタ値から計算式クラス*3で2次計算というような事をしています(・ω・)


っで、同じようなことをやってみたい人は、とりあえず上記の書籍を購入することをお勧めします。

英語やvSphere SDKのオブジェクトモデルがよくわかんなくても、各処理のサンプルソースが載っているので、それを見ていけばなんとかなるんじゃないかと思います(・∀・)

仕事の成果の流用:グラフ(チャート)表示

グラフ表示も、ちょっと前に作った物の流用です(・ω・)

要は、RRDToolもどきの、.NET製のビットマップ作成ライブラリです。

ソースの構成は下記の様な感じで。

f:id:machi_pon:20100923220958p:image

GraphBuilderがビットマップ作成のコアで。

サイズ、マージンにフォントや色なんかのプロパティを設定して、下記のようなIGraphHistoryのListを渡してやると、その内容に応じたグラフを作ってくれるという作り。

public interface IGraphHistory
{
    DateTime Clock { get; }
    decimal Value { get; }
}

っで、単にグラフのビットマップを作るだけなら単純な話なんですが、各パフォーマンス値によって目盛りのふりかたやスケーリング、グラフの描画方法(線、塗りつぶし)を変えるとなるとやっかいになってきて(´д`;)

その辺をStrategyで実装しているのが他のファイルになります。

っで、今回のVM用のグラフ表示では、それら各種Strategyを下記の様な定義で使用しています。

new GraphDef()
{
    No = 1,
    CounterId1 = Counter.CpuUsage,
    Display = "CPU使用率",
    Render = new LineRender() { LineWidth = 1.7f },
    Range = new FixRange( 10000 ),
    Separator = new FuncSeparator( 1000, _ => _ + 1000 ),
    TextFormatter = new NumTextFormatter() { Unit = "%", NumDigits = 2, Divisor = 100 },
    SeparateTextFormatter = new NumTextFormatter() { Divisor = 100 },
},      
...
new GraphDef()
{
    No = 5,
    Display = "ネットワーク受信/送信",
    CounterId1 = Counter.NetRecv,
    CounterId2 = Counter.NetSend,
    Render = new LineRender() { LineWidth = 1.7f },
    Range = new SizeRange(),
    Separator = new FuncSeparator( 16, _ => _ * 2 ) { Separets = 8 },
    TextFormatter = new SizeFormatter() { Suffix = "B/s", NumDigits = 1 },
    SeparateTextFormatter = new SizeFormatter() { NumDigits = 1, OptionalDotZero = true },
},
new GraphDef()
{
    No = 6,
    Display = "稼働時間",
    CounterId1 = Counter.SysUptime,
    Render = new FillRender(),
    Range = new TimeRange(),
    Separator = new TimeSeparator(),
    TextFormatter = new TimeFormatter() { NumDigits = 2 },
    SeparateTextFormatter = new TimeFormatter(),
},
new GraphDef()
{
    No = 7,
    Display = "VM総数/稼働数",
    CounterId1 = Counter.VmTotal,
    CounterId2 = Counter.VmActive,
    Render = new LineRender() { LineWidth = 1.7f, LineType = LineType.Max },
    Range = new CeilRange( 5 ),
    Separator = new FuncSeparator( 1, _ => ( _ == 1 ? 5 : _ + 5 ) ),
    TextFormatter = new NumTextFormatter() { Unit = "vm", NumDigits = 1, OptionalDotZero = true },
    SeparateTextFormatter = new NumTextFormatter(),
},

CPUを例に取ってみると、描画は線(LineRender)で、上限値は100%の固定(10000というのは、VMwareが使用率を0.01%単位の整数で返すから)、横軸の線は10%(1000)単位で描画して、グラフ下部の現在値や平均値については値を100でわったものに%をつけて描画、っというような内容になります(・ω・)

他の例で言えば、ネットワークの送受信なんかは2の倍数単位でスケールの計算と、K、M、Gとかの単位で文字を描画しますし。

稼働時間は秒、分、時、日(d)〜等の人間が見やすい単位でスケール計算、VM数については5単位でのスケール計算という風な感じになっています。


まあ、このライブラリについては、汎用化するにはもう少し各種プロパティの整理が必要というのと、名称についてもちょっと見直しが必要かなという感はありますが(´・ω・`)

まとめ

っということで、仕事の成果を流用してVMの稼働監視を行うアプリケーションができたのでした。

若干ネタ的な部分があったり、正直実用性は?な部分もありますが。

だって、これとは別にMuninやZabbixも動かしてるし(・∀・;)

まあ、ダッシュボードを見せて、笑いを取るために作ったという意味合いが強いので、そんなんで良しとします。


っで、あとちょっと思ったのだけど、普段事務システムしか作ったことが無いような人も、たまにはこういうおもちゃ(?)を作ってみるのも良いと思うのよね(・ω・)

なぜかというと、こういうものを作るためのノウハウというのは、事務システムを作っているだけでは学べない部分が多いから。

例えばデザパタの知識なんかも、ありがちな事務システムとか作る上では、ぶっちゃけ必要なかったりもますが。*4


でも、だからと言って、そういう知識が必要無いわけではなくて。

むしろこれからの時代は、アドホックなシステム構築のためにも、そういった知識は必須というか常識になるわけで、こういうのをサクサクっと作れない人は、危機感を持った方が良いと思うのよね(・ω・;)

っということで、みんな趣味でミドルやフレームワークを実装してみるのだ、つって(・∀・)

*1:補足すると、この値はホストがゲストに割り当てている値の意味合いなので、例えば同じCPU使用率と言っても、Webサービスから取得できる値と、ゲストOS上で認識できる値では、値も意味合いも異なりますが(・ω・)

*2:警告灯がわりに(・∀・;) 個人的にはJAM Projectあたりを希望(・∀・)

*3:実装としては、稼働VM数/VM総数を計算するHostVirtualMachineCalculatorクラス、DatastoreやVMのDisk情報からディスク使用量等を計算するHostDiskCalculator、GuestDiskCalculatorクラス等を用意

*4:その代わり、むしろアーキテクチャパターンはちゃんと学べ(`・ω・´)

2010-08-31

[][]JAX-RSを使ってみた、からの、フレームワーク的は話を少し

RIAなアプリケーションを作ることになって、クライアントはSilverlightで良いんだけど、サーバはLinuxでも動かすと言うことで。

どういう構成にしようかと検討した結果、とりあえずSpring+RESTEasyで進めてみるぽにしてみたのでした。

っで、JAX-RSを使ってみての感想を少し書いておきます(・ω・)

JavaによるRESTfulシステム構築

JavaによるRESTfulシステム構築

JAX-RS、Javaなフレームワークの中ではだいぶマシな考え方なんだけど、なんでRESTという範囲での話なのかしらね(・ω・)?、っというのがあって。

どうせならもう少し枠を広げて、Web MVC的な部分を含めてJava MVC的なものを規定すれば良いのに(・∀・)、っとか。

まあ、今回はAPIサーバを作るという目的だったので、事足りたんですが…(・ω・)

フレームワークを選ぶとき

今、JavaでWebアプリを作る場合に、プレゼンテーション層にどのフレームワークを選択するかという問題があって(´・ω・`)

まあ、フレームワークの選定作業というのは、純技術的な要素やシステム種別(イントラ事務系か、コンシューマ系かとか)以外にも、システムのライフサイクル、要員のスキルレベル、学習コスト、それに会社間の関係とかまで考慮するものですが。

とりあえずその辺の話はおいておくとすれば、Web MVC系のものに関しては、変に凝ったものよりも、シンプルかつ拡張ポイントで自由がきくものが欲しいというのが自分の希望(`・ω・´)

モダンなフレームワークの構成

FilterでRoutingの処理をして、Model binding(http request, session, cookie, route data)、Validationを行い。

そのModelを引数にControllerを呼び出して、戻り(Navigation)によってView(jsp、xml、json、redirect、...)を選択するっと、…まあ、モダンなフレームワークはだいたい似たような構成をしていますが(・ω・) *1

自由がきくという意味では、処理は拡張ポイントを利用したPiplineになっていて、BindingやValidationもPluginとして構成されていて。

後は、AnnotationやDescriptorがあるという感じで(・ω・)


言い方を変えると、それだけ用意すればWebフレームワークなんて作れますよ、なんつって(・∀・)

Javaの場合、Commonsをどうするか(どこまで自前で用意するか。まあ、必要なのはDescriptor、Converter/Mapperくらいだけど)の問題もあるけど(・ω・) *2

っで、拡張ポイントがちゃんと設計できているものならば、Controller factoryに任意にコンテナを使ったり、独自のRenderingを行うViewの追加とかは簡単にできるので、そこはCoreから分離してExtensionで、みたいになっているのが理想かな。

…まあ、ぶっちゃけ、自分的にはASP.NET MVCのまんまコピーがあるのが一番嬉しいんですけどね、なんつって(・∀・;)


っで、自分の希望の話はおいておいて、現実的に今あるもので選択しろと言われたら、Spring MVC 3にしちゃうかも、っとか(・∀・;)

Java、JSRの仕様と実装の分離

あと、今更ながらになんだかな〜と思ったのが、Java、JSRの仕様と実装の分離について。

仕様を策定して、実装は分離すると言うことについては、例えばスモールデバイスのAPIだとか、エンタープライズミドルのAPIなんかについては意味があると思うわけですが。

でも、ぶっちゃけフレームワーク的なものでそれをやられても、無駄に複数の実装ができたり、実装毎の拡張仕様の違いがあったりだとか、面倒なだけだと思うんですよね(´・ω・`)

JAX-RSの話

そして話をJAX-RSに戻しますが。

拡張ポイント的な部分は弱いな〜と思いますた(´・ω・`)

あと、他のWebフレームワークにもありがちなんだけど、URLのパスをアノテーションで指定するんじゃなくて、ちゃんとしたRouterを作って欲しいと思ったり(´・ω・`)

パスパラメータの処理と、パスにマッチした時のハンドラへのディスパッチはRouting専門のFilterにやらせるべきで。むしろweb.xmlにはRoutingFilterのみを登録したい(`・ω・´)、つって。

Java製のソーシャルっぽいWebアプリにありがちだけど、Url Rewrite Filterで色々ごまかすとかカコワルイ(´・ω・`)

パーシステント層についても

っで、プレゼンテーション層の話をしたんで、ついでにデータアクセスの話についても少し。

個人的には、データアクセスについては柔軟にやりたいんで、本当はArel的なSomethingが良いんだけどというか、(DBから作成した)式メタデータクラスを使ってクエリ構築する感じが希望ですが。

後、マッパー部分はGrouping対応を標準で用意してくれれば、自分的には事足りそうな予感もしますが(・∀・)

SQL外部化型のORMは小規模事務系には良いかもしれないけど、パラメータの有無による条件の有無とかが増えてくるとなんか面倒だし、それならいっそストアドにして結果だけマッピングするんで良いよと、思ったりもして(´д`)


小規模データ(<10万)、限られたユーザ数であれば、ORMなんてなんだって良いよ、…等という堕落した思考も世の中にはありますが(´д`;)

いや、トランザクションストラテジーの話はあるか、意図しての設計ならともかく、そこをルーズにはできないというSI脳(・ω・;)


あんまりオブジェクトオブジェクトしたものも好きではなくて。

っというか、物事の隠蔽度合いがイマイチというか、フォーカスしている領域が狭すぎると感じるというか。

SQLを意識しなくて良いからと言って、だらだらとオブジェクトの操作を書いて、それをもってドメインなんちゃらと主張してみたところで、自分はまったく嬉しくありませんが(・∀・;) *3

あげくは、最後の手段として固有のxQLみたいなものを持ち出すのは、脳わいてんのか?、……みたいなのはよくある話(・ω・;)

本当にオブジェクトオブジェクトしたパーシステントフレームワークが有用なのは、人によっては愛憎色々なオブジェクトデータベースを使って、RDBよりオブジェクトデータベースがむいているタイプのシステムを作るときだけだと思うんですけどね。

っで、まあ、論外な話とかは無視するとしても、堕落が始まってしまうのは、その程度の要求で済んでしまうシステムが大多数で、そういう開発に従事している人間が大多数だからだ、……っというのはこれまでのお話(・ω・;)

今時は、そんなつまらない設計が必要な仕事は無くなってきていると思うので、どうでも良いんですけどね、とか、投げやり。


…などと、Twitterのつぶやきから再構成した日記でした(´д`;)

*1:その一方で、特定のケースに特化した部分があったりして、そこが使いにくい理由になっていることもあるのだけど(´д`;)

*2:この辺がフレームワーク毎に個別にあったりするのも、Javaのイヤンな所(´д`;)

*3:っというか、ドメインなんちゃらってそういうもんじゃねーダロと(´д`)

2010-06-15

[]HudsonでASP.NETアプリをデプロイする設定、.NET Framework 4版(・∀・)

Hudsonを使って、CIでビルドの実行やテストを流す他にも、ビルドしたWebアプリをIISにデプロイして、最新版の動作が確認できるような設定をしています(・∀・)

っで、先週、開発プロジェクトをVisual Studio 2010/.NET Framework 4対応にして、Hudsonのデプロイ設定もそれに併せて変更したのでその時のメモです。


なお、デプロイ先の環境はWindows Server 2008 R2のIIS 7.5になります。

また、デプロイにはmsdeployを使用しますが、Visual Studio 2010がインストールされていない環境については、別途msdeployをインストールしておく必要があります。

っで、その辺のインストールやHudsonの環境構築の話は省略して、Hudsonの必要な設定の所だけを記述します(´д`;)


あと、以下の内容はHudsonとIISが同じWindows Server上で動作している場合の設定になりますが、msdeployのオプションを指定すればHudsonからリモートのWebサーバーにデプロイも可能だと思います(・ω・)


っということで、Hudsonで行う設定について。

MSBuild設定

プロジェクトのビルドはMSBuild Pluginを使用するので、[Hudsonの管理]-[プラグインの管理]から追加しておきます。

次に、MSBuildの場所を設定するために、[Hudsonの管理]-[システムの設定]を開きます。

[MSBuild Builder]欄で[追加]ボタンを押下して、下記の様な設定を行い、MSBuildの場所を指定しておきます。

nameMSBuild v4
Path To msbuild.exeC:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe

f:id:machi_pon:20100615093858p:image

ジョブの設定

ASP.NETアプリ用のジョブは「フリースタイル・プロジェクト」を使って作成しています。

プロジェクトの作成後、ダッシュボードから該当のジョブを選択し、[設定]画面を開きます。

設定を行うのは下記の項目になります。

  • ソースコード管理システム&ビルド・トリガ

リポジトリからソースコードを取得するための設定を行います。

使っている[ソースコード管理システム]と運用ポリシーにあわせた[ビルド・トリガ]の設定をしてください。

例えば、[ソースコード管理システム]には[Subversion]を選択して[リポジトリURL]を設定し、[ビルド・トリガ]は[SCMをポーリング]に設定する等(・ω・)

  • ビルド-プロジェクトのビルド

プロジェクトをビルドして、配置パッケージを作成するための設定を行います。

これは、Visual Studio上でプロジェクトのコンテキストメニューから[配置パッケージの作成]を行う作業に相当するものです。


Hudson上でジョブの設定画面の[ビルド]項目に移動し、[ビルド手順の追加]から[Build a Visual Studio project or solution using MSBuild.]を選択します。

MSBuildの項目が追加されたら、各入力欄には次のような値を入力します。

MsBuild VersionMSBuild v4
MsBuild Build FileD:\Build\Hudson\jobs\MyApp\workspace\trunk\MyApp.Web\MyApp.Web.csproj
Command Line Arguments/t:Package /p:Configuration=Release

MSBuildの対象とするプロジェクトファイルはHudsonがソースコード管理から取得したものを指定します。

引数のConfigurationで指定しているReleaseはビルドの構成です。

Release以外に環境固有の構成(Stagingとかの構成を作って、Web.Staging.configを使う様にするとか)を作成していれば、それを指定します。


これで、ビルドが実行されるとobj\Release\Package\MyApp.Web.zipに配置パッケージが作成されます。

  • ビルド-IISへのデプロイ

msdeployを使用してIISへ作成したパッケージをデプロイする設定を行います。

[ビルド手順の追加]から[Windowsバッチコマンドの実行]を選択し、[コマンド]欄に次のような値を入力します。

"C:\Program Files\IIS\Microsoft Web Deploy\\msdeploy.exe" -verb:sync -source:package='D:\Build\Hudson\jobs\MyApp\workspace\trunk\MyApp.Web\obj\Release\Package\MyApp.Web.zip' -dest:auto -setParam:"IIS Web Application Name"="MyWebSite\MyApp"

f:id:machi_pon:20100615093925p:image

これで、msdeployを使用してIISへ配置パッケージのデプロイが行えます。

なお、配置パッケージの作成時にPackageディレクトリに作成される「プロジェクト.deploy.cmd」ファイル(内部的にmsdeployを使用)を使ってもデプロイは可能ですが、ここでは、同じくPackageディレクトリに作成される「プロジェクト.SetParameters.xml」の設定を使用せずに、パラメータを自由に指定するためにmsdeployを直接使用しています(・ω・)

その他

msdeployの使用時のTipsについてです。

  • Webサイトのルートへのデプロイ

WebサイトのサブディレクトリにWebアプリケーションをデプロイするのではなく、Webサイトのルートにデプロイする場合には"IIS Web Application Name"="MyWebSite\MyApp"ではなく"IIS Web Application Name"="MyWebSite"の様に指定します。

  • 異なる物理パスへのデプロイ

デプロイを行うと、サイトの物理パス下にファイルがコピーされます。

サイトの物理パスとは異なる場所にデプロイを行いたい場合、IISマネージャーを使用して事前に[アプリケーションの追加...]を行い、物理パスを指定したアプリケーションを追加しておきます。


っということで、これで動作確認用サイトへのデプロイ処理が自動化できまスタヽ(´ー`)ノ

この設定を行っておくと、開発者以外の人が最新環境を確認するのに良いのですよ(・∀・)

2010-02-21

[][]6+ .NET libraries Compact Framework developer should know about.

思いつきネタ(・∀・)

元ネタはこちら。

Compact Frameworkで使うライブラリを紹介してみますという感じで(・ω・)

主にSDFの紹介というか、ライブラリと言うよりCompact Frameworkで機能をどう実現するかという話な気もしますが。

Smart Device Framework

http://www.opennetcf.com/Products/SmartDeviceFramework/tabid/65/Default.aspx

何はなくともまずはこれでしょう(・∀・)

Full Frameworkに慣れた人がCompact Frameworkを初めて触ったときの反応は、


  l::::::::::::::::::|_!::lヽ:::::::::ハ::::::::::::::::::::::::::::::::i、::! ノ
  !:::::::::::::::::l-‐ェ!;ト ヽ:::::l ´!:::::::::::::::::::::::::::::l ` ヽ   幺ク 亡 月 |  ┼‐ .|] |]
  l:::::::::::::::::「(;;;)ヽ、__、::レ'´l:::::::::/l、:::::::::::::l   /   小巴 三l三. ヽ_ノ / こ o o
  !:::::::::/l:::l__,,,rタ"゙、;!)、__!::::/ノ 〉、::::::::l   \
   l::::/ lヽ!    _ _   l;/´  ! >、::l   /  .NET Compact Frameworkの貧弱さに絶望した!!
  ノノlヽ、_!    r――‐┐   /_ノ:::|  /
    l::::::>、   レ,二二ェ!  /i:::::::::::l   ̄ ̄|_     /ヽ、  /\   /\    /
    l:::/ /::ヽ、 `ー-―-' ,ィ'::::!\:::::l    (ヽ、//\/   \/   \/   \/
    レ' ム-''´lヽ、  _,,./! ゙ヾ!__ヽ!    ヽ´ヽ、ヽ
            !   ̄     レ;'´  |  (,ゝ、 \ ヽ l、
        /| _,,.-/´  ;; .,,,-!  ヽ、 ヽ、 | | ! l
       / 斤'"〇 /´    ,;;:''" _,l_   ヽ ヽ/  l | l
      /; l、」_,,/     '' ゙;;/  ヽ、   〉  `ヽ  l/
      /!,r''´!/  /     ';,/"゙''':;,,,,;;'' \ /     ,!
    / l ,;;  |l  /`'';, ,,   /   ,;;''"゙''   l     /

そんな不満を解消してくれるのがSmart Device Framework(以下SDF)です(・∀・)


Native APIをラップしたり、Compact Framework(以下CF)ではサポートしていないメソッドのオーバーロードを提供してくれたりと、CFの開発者には必須と言っても良いライブラリじゃないかと。

例えばどんな機能があるかについて、ちょっとだけ書いてみますが。

OpenNETCF.Windows.Forms.dll

CFでFormsアプリケーションを作ったときに困るパターンとして、以下のようなものがありますね(´・ω・`)

  • IMessageFilterがサポートされてねーよ(♯・∀・)
  • Application.Run()がRun(Form)しかサポートされてねーよ( ゜Д゜)ハァ?

ご安心ください、そのためのSDFです(`・ω・´)

SDFではApplicationクラスの代替としてApplication2クラスが用意されています。

Application2クラスでは、IMessageFilterのサポートも、Application.Run()のForm引数無しバージョンもサポートされています。

いったいどういう仕組みなのかと言えば、GetMessage()、TranslateMessage()、DispatchMessage()辺りを使った独自のメッセージポンプを用意するという、生Win32アプリみたいな事をやっているんですよね(゚Д゚)


その他のForms関連のネタとしては、OwnerDrawnListにあたりとか。

Windows CEだと、そもそもNativeレベルでリストボックスがオーナードローをサポートしていないので困ったもので(´・ω・`)

ちょっとしたグラフィカルなリストを作ろうにも、ListViewを使ってオーナードローをする(CFなら要Hook)か、フルスクラッチでControl派生のリストを作るかになるわけですが。

っで、Control派生のリストとしてSDFで提供しているのが、ListBox2やそのベースであるOwnerDrawnListになります。

まあ、要求によっては自分でスクラッチでControl派生のリストを作った方が良いケースもありますけどね(・∀・)y‐~~


ちなみに、CFでのメッセージのフックについてはこの辺も参照に。

そうそう、フックと言えばSDFではKeyboardHookクラスなんかも用意されていますが。

SDFに似たキーボードフッククラスの作り方についてはこの辺に書いているので、そちらも参照に。

OpenNETCF.dll

CFではサポートされていない機能の代替品が色々入っています。

メソッドのオーバーロードが足りない人用に、Enum2、Environment2だとか(´д`)

CFにはBackgroundWorkerが無いんだよな〜という人には、OpenNETCF.ComponentModel.BackgroundWorkerだとか。

簡易的なログ出力とかしたいんだけどな〜という人には、OpenNETCF.Diagnotics下にCFには無い機能の代替クラスがありますよ(・ω・)


あと、CFで困るのがマーシャリング関連の貧弱さ(´・ω・`)

APIを叩くときにその貧弱さに泣きたくなることもありますが。

そんな貴方にはOpenNETCF.Runtime.InteropServices.AdvancedMarshalerをどうぞ。

これは何かと言えば、AdvancedMarshaler派生クラスのメンバ変数とbyte[]配列の変換をサポートしてくれるもので。

P/Invokeでstructを渡す代わりに、AdvancedMarshalerを通じてシリアライズしたbyte[]配列を使う目的で使用します。


その他のものとしては、OpenNETCF.ToolHelp名前空間にはModuleEntry、ProcessEntry、ThreadEntryクラスとかがあって。

つまりtoolhelp.dllのラッパーなわけですが、これをそのまま使うだけでタスクマネージャが作れますね(・∀・)

OpenNETCF.Drawing.dll

CFでは描画処理の貧弱さもまた困りもの(´・ω・`)

特にCF 1.0時代はGraphics.GetHdc()すら未サポートだったりしてね(つд`)

そんな経緯もあって、OpenNETCF.DrawingではGDIラッパとして独自にGraphicsEx、PenEx、FontEx、BitmapExなんかを用意しています。

OpenNETCF.Net.dll

OpenNETCF.Net.NetworkInformation名前空間は微妙に気合いが入っていますけど。

要はiphlpapi.dllのラッパークラス群で。

Pingクラスみたいに単純なものをはじめ、ネットワークインタフェース・アダプタ周りのクラス群がFull Frameworkに似せた形で用意されていますね。

この辺のクラスの使い方については、SDFのサンプル中のNetUI、GetNearbyAPListを参考にすれば良くて。

簡単にNetStumbler的なものが作れることがわかると思います(・∀・)


あと、OpenNETCF.Net.FtpにFTP関連のクラスもあるようですが。

ただこれ、Full Frameworkに併せてFtpWebRequest/FtpWebResponse型の実装なので、むしろこっちの方が使い安いんじゃないかなと思ったり(・ω・)

http://community.opennetcf.com/forums/t/10502.aspx

OpenNETCF.Net.Mail.dll

メール送信用のSmtpClientクラスということで。

HTTPについては標準のHttpWebRequestがあるし、後はFTPにSMTPもライブラリとしてあれば、スマートフォンアプリとしては十分ってとこですかね(・∀・)?

OpenNETCF.WindowsCE.dll

OpenNETCF.WindowsCE名前空間にあるのは、システム情報、メモリ情報、パワーマネジメントとかその辺のAPIラッパーのクラス群です。

あと、OpenNETCF.WindowsCE.Notificationには通知処理(CeRunAppAtTime()等)やLED制御なんかのラッパーも用意されているので、自前で定義を書く必要は無くなります。

OpenNETCF.WindowsCE.Messaging.dll

メッセージキュー関連のラッパーはこの辺にあります。

CEの場合、OSで用意しているメッセージキューに接続してシステム周りのイベントを拾うとかよくやるので(・ω・)



…っとSDFについて紹介してきたわけですが、実は自分はSDFを使っていなかったりしてね(´д`;)

なぜかというと、必要とするものについては同等の私家版を作ってしまっているから(・∀・)

ネットワーク関連については、FtpClientやSmtpClient、PopClientみたいなものまで。


SDFって、SDFとなる以前のCF 1.0時代に初期のネタがMSDNの記事に載っていたり、1.xの頃にソースを参考にしていたりしたので(・ω・)

CF 1.0時代からアプリを作ってきた人としては、必然的にカスタム版のライブラリが出来てしまったという次第。


ちなみに、SDFのソースが見たい人は$50でStandard Editionを購入すればソースもついてくるのでどうぞ(・∀・)

Expressions

https://source.db4o.com/db4o/trunk/db4o.net/Libs/compact-3.5/System.Linq.Expressions/

CFではSystem.Linq.Expressionsをサポートしてないよとお嘆きのあなたにはコレ(`・ω・´)

これがあれば、CFでもExpressionsが使えますよ、フフフ...。


過去の日記ではこちらも参照。

タイプセーフにアドホックな条件が指定ができるライブラリを作る場合、やっぱりExpressionsが無いとね〜(´д`;) (Compact FrameworkでExpressionsを使う)

QRCode Library

http://www.codeproject.com/KB/cs/qrcode.aspx

モバイルと言えばQRコード。

これを使えばEncodeもDecodeも出来ますよ(・∀・)

ちなみにVS2008でビルドして怒られるときの対応はこうね(・ω・)

MemoryStream memoryStream = new MemoryStream((byte[])Resources.ResourceManager.GetObject(fileName));

ところで、業務用ハンディなんかで2次元対応のレーザースキャナを積んでいるモデルがありますが。

ああいうのは、デコードライブラリもビルトインされているので、APIを呼び出せば文字列としてデコード結果を取れるので楽ちんポン。

GPS

GPSも、昔は自分でシリアルポート読んでいたわけですが。

今ならGPS Intermediate Driverがあるので楽ちんポン(・∀・)


CFからの利用については、SDKのサンプルにあるMicrosoft.WindowsMobile.Samples.Locationをそのまま使うのでも良いんじゃないかと思ったり。

サンプルの場所は以下。

Windows Mobile 6 SDK\Samples\PocketPC\CS\GPS

GPS Intermediate DriverのAPIってそもそも4つしか無いし。

GPSのオープン、クローズ、位置情報の取得、デバイス情報の取得と単純なもので。

上記サンプルは、バックグラウンドスレッドでWaitForMultipleObjects()しながら、位置情報/デバイス情報に変化があったら非同期でイベントをあげてくれるというものです。



っで、その他、60+で紹介されているもので、CF対応しているものについても少し。

DotNetZip

http://dotnetzip.codeplex.com/

ZIPの操作はこれで。

FileHelpers

http://filehelpers.sourceforge.net/

業務用のハンディだと、データベースではなく、上位からダウンロードした固定長ファイルをそのまま扱ったりすることもあって、こういうものは便利に見えますが。

ただ、CFが載るようなデバイスだと、リフレクションを使った処理のコストも馬鹿にならなくて(´д`;)

PCでやっている分には気にならないような処理も、CFではどうするか検討した方が良いことも。

自分としては、定型処理の簡略化が目的であれば、T4 Template使ってコード生成した方が良いと思うのよね〜(・∀・)y‐~~

System.Data.SQLite

http://sqlite.phxsoftware.com/

データ関連でついでに、SQLiteもCFに対応しています。



…っということで、Tech・DaysでWP7の開発ネタも聞けるのかな?、っというこの時に、今更CFネタなんかを書いてみたわけですが(´Д`)


ちなみに、自分はWP7での互換性の切り捨て歓迎派だったりして(・∀・;)

前に、スマートフォン開発者には「Win32を見せるな」「センサーやHW操作は抽象化したAPIを用意しろ」「UIはWPFにしろ」とか言っていたりもして。

アプリ開発者の裾野を広げて、つまらんことに煩わされずアイディア勝負にアプリを作って貰うには、そのくらいの割り切りは必要だろうという意見で(・ω・)

まあ、それはあくまで「スマートフォン」としての話であって、Windows CEを載っけた業務用ハンディーターミナルなんかまで余計な制約が出てきたりすると、それは困るんですけどね(´д`;)


ところで今週は高橋さんの本も発売しますね(・∀・)

Windows Mobile 6.5 アプリケーションプログラミング

Windows Mobile 6.5 アプリケーションプログラミング