Archive for 11月, 2011
Netty(というか、TCP/IP層でのサービスだから)だと、
いとも簡単に同じポートで複数のサービスに対応できる。
これができるのは結構おもしろそうだ。
つまり、80番ポートでhttpもhttpsも受け付けることが可能なのだ。
サンプルは、本体のソースの中にあります。
もちろん、ブラウザ側がそんな器用なことができるわけではないので、
これだけでは意味が無いが、
HTTP層の上で複数のサービスを実装することはあるだろう。
HTMLを要求するリクエストと、AjaxやXMLを要求するリクエストで、
作りを分けるということもできる。
アイデアしだいで今までのWEBアプリの概念にはないアプリが作れるのではないだろうか?
今まで、E-Mobileのデータ通信カード(と言ってもカードの形状はしていませんが・・・)を使っていました。
だいぶふるいので、4.2Mのタイプです。
そして、このたび、S42HWに変えました。
なぜか、今までの契約に加えて、新たに契約を追加すると、合計金額が安くなる(ただし、期間縛りがついてしまいますが・・・)
ということで、今までのデータ通信カードは、お役ご免となり使いません。
(価格に裏技みたいな組み合わせがありすぎて、難しいですね。店員には何度も確認してしまいました)
一方で、S42HWといういわゆる電話を今後は、データ通信専用として使うようにしました。
従って、使わないときには電源から落としてしまっています。
まず、データ通信カードを使っていて、今ひとつ使いずらいなーと思っていたことで改善されたこと。
1)通信できるエリアなのかそうではないのかわからない(わかりずらい)。
単体で通信できるし、マークに3Gなのか?H(7.2Mモード)なのかも表示されます。
(通信カードを使っているときは、接続したままスリープモードにすると、再開したときにつながらないなど、ストレスがありましたが)
だいぶ改善されました。
2)USBのポートを使わない。
持ち運びのノートPCでUSBポートを占有することは1つでもない方がうれしいです。
テザリングは便利です。
また、よかったことと、当初、心配したが別に問題なかったこと。
1)テザリングで、複数の端末からアクセスが可能に。
私のIS03もこれでアクセス可能に。
電車の中では、AUの回線よりもつながりがよいように思います。
2)電池の持ち
とりあえず、ノートPCの電源(3,4時間)よりは持つようです。
使い方が、いつも電源をOFFにしているからでしょうか?
それでも、とりあえずは問題ありません。
そして、意外な懸念点。
1)テザリングしていると、端末自体があったかくなります。(ほっかいろ程度くらいに)
別段、問題と言うほどでもないのですが、「あー、動いているのだなー」と実感します。
次に、テザリングという小さいながらも、複数の機器でネットワークをくめることから、
ちょっとして、持ち運びサーバとして作り込みたいという気がしてきました。
まずは、WEBサーバや、Wikiサーバとしてこの、S42HWが機能しないか?を確認したいと思います。
WEBサーバとしては、nettyを使ってある程度確認していますので、大丈夫だとは思いますが・・・・
(電池の持ちがよくなくなるかもしれませんが・・・・)
Android(IS03)上でnettyを動かしてみました。
特に問題なく動きます。
ちなみに、nettyのバージョンは3.2.5です。
今回は、netty上でTCP/UDPでのsyslogdのサーバを作成して、
外部(wifi上の別の端末)から、syslogのメッセージを受け取らせるということをしてみました。
Androidは、監視端末として作り上げると結構使えるのではないかなと思っています。
それで、そんなテストをしてみました。
受けたメッセージによって、
音を鳴らすとか、電話をかけるとか、ショートメッセージを送信とかもできるはずなので。
Javaでよく使っているし、使えるが、作りはしないもの。
それが、私にとってはアノテーションです。
今まで、それで問題ないのではとおもっていましたが、JavaのRESTful、つまり、JAX-RSをみるにあたり、
改めて、見てみる事にしました。
さて、RESTfulで、Javaのソース内で、
@GET
@Path("/foo")
public void foo(@PathParam("arg1") String arg1){
}
みたいな記述ができるのですが、これらの情報をどうやってとっているのかとか、ある程度まとめました。
まず、アノテーションの作成方法ですが、
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassAnnotation {
}
のような感じで、アノテーションを作成します。
interfaceのような宣言ですが、頭に@がついています。
これで、
@ClassAnnotation
が使えるようになるわけです。
ただし、ソースのどこでもつかえるという訳ではなく、
この指定はクラス宣言のアノテーションとして記述できますが、メソッドのアノテーションにはできません。
そのあたりを決めているのが、@Targetです。
その、@Targetですが、ElementTypeの定数をセットできます。
複数の場所に記述できるようにしたければ、
@Target({ElementType.TYPE, ElementType.PARAMETER})
のようにして、複数指定します。
また、
@Retention(RetentionPolicy.RUNTIME)
を記述しないと、実行時に取得できません。
詳しくは、こちらをご覧ください。
では、実際に使って見ます。
アノテーションの利用
@ClassAnnotation
@ClassValueAnnotation("this is annotaion value")
public class ClassAnnotItem {
}
ClassAnnotationの宣言
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassAnnotation {
}
ClassValueAnnotationの宣言
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassValueAnnotation {
String value();
}
のような記述をしたものから、実際にプログラムで何というアノテーションが指定されているのかを調べます。
public static void classAnnotationSample1(){
Annotation annotList[] = ClassAnnotItem.class.getAnnotations();
System.out.println("annotaion size is [" + annotList.length + "]");
for(int i=0; i<annotlist .length; i++){
Annotation annon = annotList[i];
System.out.println("annotation class is [" + annon.annotationType().getName() + "]");
if(annon instanceof ClassValueAnnotation){
ClassValueAnnotation annonVal = (ClassValueAnnotation)annon;
System.out.println(" |--> value is '" + annonVal.value() + "'");
}
}
}
を実行した結果が
annotaion size is [2] annotation class is [annot.ClassAnnotation] annotation class is [annot.ClassValueAnnotation] |--> value is 'this is annotaion value'
こんな感じに取得することができます。
たとえば、メソッド指定のアノテーションの場合には、
public static void methodAnnotationSample3(){
// メソッドのアノテーションが調べたければ、メソッドを取得してから
Method methodList[] = SubclassMethodAnnotItem.class.getDeclaredMethods();
System.out.println("method size is [" + methodList.length + "]");
for(int i=0; i<methodlist .length; i++){
Method method = methodList[i];
System.out.println("method name is #" + method.getName() + "()");
for(Annotation anno: method.getAnnotations()){
System.out.println(" |--> annotaion class is [" + anno.annotationType().getName() + "]");
}
}
}
のように、ReflectionをもちいてMethodを取得してから、そのメソッドに対してClassと同じように取得すればいいわけです。
では、
@ClassKVAnnotaion(boolVal=true,intVal=10,strVal="this is string value")
public class ClassKVAnnotItem {
}
のように、キーと値を指定できる記述ができるアノテーションを作るにはどうしたらよいかと言えば、
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassKVAnnotaion {
String strVal();
int intVal();
boolean boolVal();
}
のようにすればよいわけです。
では、
public class FieldAnnotItem {
@FieldAnnotation("sample1")
private String privateField;
@FieldAnnotation("sample2")
protected String protectedFideld;
// デフォルトを指定しているので、値を省略できる
@FieldAnnotation()
public String publicField;
}
のように、値を入れることもできたり、省略することもできるアノテーションはといえば、
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldAnnotation {
String value() default "this is default value";
}
のようになりdefaultの指示をすることになります。(なんか、SQLみたいです)
ちなみに、valueだけは(value=”foo”)とも(“foo”)とも記述できます。
それ以外はだめなようです。
public static void fieldAnnotaitonSample1(){
//
Field fieldList[] = FieldAnnotItem.class.getDeclaredFields();
for(Field field: fieldList){
System.out.println("field is [" + field.getName().toString() + "]");
Annotation annonList[] = field.getDeclaredAnnotations();
for(Annotation annot: annonList){
System.out.println(" |--> annontaion is [" + annot.annotationType().getName() + "]");
if(annot instanceof FieldAnnotation){
System.out.println(" |--> value is '" + ((FieldAnnotation) annot).value() + "'");
}
}
}
}
を実行すれば、
field is [privateField] |--> annontaion is [annot.FieldAnnotation] |--> value is 'sample1' field is [protectedFideld] |--> annontaion is [annot.FieldAnnotation] |--> value is 'sample2' field is [publicField] |--> annontaion is [annot.FieldAnnotation] |--> value is 'this is default value'
となり、キチンとデフォルト値がとれています。
最後は、引数の中のアノテーションですが、
public class ParamsAnnotItem {
public void check1(@ParamAnnotation("id") String id, @ParamAnnotation("value") String value){
}
public void check2(@ParamAnnotation("arg0-0") @MultiAnnotation String id){
}
}
のように記述したあるものとします。
ここで、@ParamAnnotationと@MultiAnntationの宣言は省略します。
public static void paramsAnnotationSample1(){
// メソッドのアノテーションが調べたければ、メソッドを取得してから
Method methodList[] = ParamsAnnotItem.class.getDeclaredMethods();
System.out.println("method size is [" + methodList.length + "]");
for(Method method: methodList){
System.out.println("method name is #" + method.getName() + "()");
Annotation params[][] = method.getParameterAnnotations();
System.out.println(" |--> params length is " + params.length);
for(int i=0; i < params.length; i++){
for(Annotation arg: params[i]){
System.out.print( " |--> arg[" + i + "]:" + arg.annotationType().getName());
if(arg instanceof ParamAnnotation){
System.out.println("('" + ((ParamAnnotation)arg).value().toString() + "')");
}
else{
System.out.println();
}
}
}
}
}
を実行すれば
method size is [2]
method name is #check1()
|--> params length is 2
|--> arg[0]:annot.ParamAnnotation('id')
|--> arg[1]:annot.ParamAnnotation('value')
method name is #check2()
|--> params length is 1
|--> arg[0]:annot.ParamAnnotation('arg0-0')
|--> arg[0]:annot.MultiAnnotation
と表示されます。
まだ、細かい部分で足りない部分もありますが、
おおよその理解はできているはずです。
XMLやpropertiesファイルでの外部指定がよいのか、
アノテーションによるプログラム指定がいいのかは、
好みとケースで異なるでしょうが、
結構便利なだと思います。
Mobile版のFlash Playerが開発中止になるようです。
今後は業界はどうなっていくのでしょうか?
折しも、私もついこの間、JavaFXにもちょっと触手をのばしたのも、
多少、ここに関係がありそうな気がします。
まず、今までの単純なアニメ(写真の効果を使ったものとか、ロゴとか)は、
Flash Playerの役割は終え、HTMLで十分になりました。
となれば、Flashでゲームなどと言っているように、
よりリッチで、インタラクティブ性がたかい。
つまり、アプリの要素がより高くなるわけです。
ただ、ちょっとしたものを作るための環境が閉ざされてしまうのは、
それはそれで残念な気がします。
(まあ、開発者としてはいい方向かもしれません。素人と玄人の壁がより高くなるのですから・・・)
では、今後はモバイルはAIRということです。
もしかしたら、Windows8になると、Metroがあるので、PCでのFlash-Playerの役目もより低くなるでしょう。
だったら、Flash Playerは環境をほしいものために変えてもいいし、それができるより趣味嗜好が強い個人向けがよりターゲットになるはずです。
つまり、ブラウザでできることであればブラウザで、
ブラウザでできないのならば、PCであろうと、携帯であろうと、アプリとなるわけです。
コンシューマレベルでは、広い環境に適合する必要があるので、
ブラウザでよいかもしれませんが、
ビジネスアプリレベルでは、よりクライアントアプリの必要が高まってくる可能性もあります。
おもしろいもので、このような状況になってくると、
よりAIRの特徴が引き立てられると思います。
iPadでairxmailを使おうとしている人がちらほらと現れるということは、
サーバ側で何らかの理由でメールの送受信をしたくないということですので、
自ずと、ブラウザの限界を超える必要が現場レベルとして発生しているということです。
ただ、現在のPure ActionScriptとして作ったAIRに限界が見えているのも事実で、
このために、Native Extensionがあるわけです。
ただ、難しいのが、この手法に入ったということは、
たとえば、Androidであれば、ActionScript + Javaの知識が望まれてしまい、
そして、現実問題として結構なJavaのコードを書く必要があるならば、
すべてJavaでいいやとも思えるところです。
(どちらも知っていればなおさら、片方だけですべてをやってしまいたくなります。)
私は、AIRがWEB業界のPHPと同じようになり得ればいいなと思います。
PHPは自分でextensionをかけるわけですが、
案件ベースで今まで自分でExtensionを作って解決しようとした実例は見たことがありません。
そのためには、みんなが共通で使えるNative Extensionの部分のライブラリがいろいろとそろうことではないでしょうか?
つまり、役割分担の機能が十分に業界として機能し始める必要があるわけです。
いつの間にか、Flash PlayerからAIRに話が変わってしまいましたが・・・・
airxmailとipadを使ってアプリの構築を試した方がいらしたら、情報をください。
理由は、最近立て続けに何名かの方(日本以外も含めて)から、
エミューレータでは動くものの、実機では動きません。
と報告がきます。
私自身、ipadはおろか、iphoneも経験がないために、
困っている方へ何らアドバイスもできません。
もし、何か情報を持っている方がいらしたら、
ここでのコメントなり、直接メールでもかまいませんので、
教えていただけたら大変助かります。
別に、動く方法でなくても、同様に困っている、動かないという報告でもかまいません。
よろしくお願いします。
プログラムを管理するとなれば、
やれ、Subversionやら、Gitやら、そしてちょっと前ならば、当然、CVSが使われていたわけですが、
なぜか、それらの管理手法をサーバ管理などにつかってもらえない。
でも、機能はもっと貧弱ではあるのですが、RCSという履歴管理システムも知ってほしい。
というのも、
httpd.conf
httpd.conf.org
httpd.conf.org.20111109
httpd.conf.hon
こんな感じの管理になっており、
何がなんだかわからない。
だったら、設定をRCSで管理してしまおう。
RCSは、簡単にいえば、そのディレクトリの下のファイルを管理するCVSです。
これで、設定ファイルをコメントのゴミで埋めることはなくなり、
変更日時とコメントと変更場所の管理と、設定自体の管理は別になります。
例えば、Apacheであれば、
cd /user/local/apache2/conf
ci -l httpd.con
のように、設定ファイルを変更するごとにチェックインしていくだけなのですが、
当然、差分もみることができますし、もとに戻すこともできます。
そして、何より、
locate
find
grep
などを駆使して、
サーバの変更を後で調べることができるので、
サーバ設定仕様書みたいのがなくても(というかそれ以上のこと)、
こと1台の中で閉じている情報ならば、それで十分だったりします。
リポジトリとワークを同一にできるからこそ、お手軽にできるので
私的には結構おすすめです。
Flashのセキュリティなどをいろいろ試しながら、Nettyを使ってAIR以外からもいろいろできるようにするにはどうしたらいいかと、
考えているところに、今ひとついい方法が見つからず、
だったら、Javaだけでやる方法ということで、最近JavaFXが2.0になったこともあり、ちょっと試してみました。
ほんの二日(といっても、合計で数時間程度)ですが、その感想です。
よいところ
1.Flex ( MXML + ActionScript ) の形式に似ています。
Flexにはmxmlがあり、JavaFXにはfxmlがあります。
これが結構、考え方が似ているので、それなりに使いやすいと思います。
2.Javaなので、非同期処理がThreadとして別にできるので、イベントのスパゲティに多少なやまなくていいように思いました。
3.豊富なオープンソースが利用可能です。
私は、JavaFXをデザインとして、裏側でRESTFulな組み込みサーバでやるとよいのかなと思いました。
悪いところ
1.情報が不足。
2.FXMLに対応したエディタがないので、どこまでなにができるのかがよくわからない。
(Flash Builderはmxmlでもコード補完がきくので、それが必須なら使えないというレベルになるかも。)
3.コンポーネントがFlexにくらべ十分でない。
などでしょうか?
まあ、はやりの開発スタイルなのでしょうから、あとは時間の問題と、市場がどの程度受け入れるのか?
ということでしょう。
クライアントサイドになると、技術の善し悪しや、わかりやすさなどより、市場との適合性がすべてでしょうから、
いまのところ、Windowsでのアプリ以外であればちょっと使えないのかもしれません。
ここでは、airxlibを使ってテーブルの作成から、簡単な操作を紹介します。
まずは、大まかな流れを示します。
private var forceCreateTable:Boolean = false;
private function init():void{
var conn:SQLConnection = new SQLConnection();
conn.addEventListener(SQLEvent.OPEN,sqlConnectionOpenHandler);
var file:File = File.applicationStorageDirectory.resolvePath("sample.sqlite");
if(!file.exists){
this.forceCreateTable = true;
}
conn.openAsync(file);
}
private function sqlConnectionOpenHandler(event:SQLEvent):void{
var dbman:DBManager = DBManager.newInstance(SQLConnection(event.target),null,this.forceCreateTable);
// テーブルを作成する
dbman.registerTable("sample1","xml/sample1.xml");
}
最初は、DBを非同期モードでオープンしています。
ここで、ファイルが無いときには forceCreateTable = true としていますが、
これは、内部でバージョン管理をしており実際のDBファイルを削除してしまった場合など、
二度とテーブルの作成処理が動かなくなってしまいます。
このために、DBファイルそのものがない場合には、強制的にテーブルを構築します。
また、上で示した最後の
dbman.registerTable("sample1","xml/sample1.xml");
という行で、テーブルの構築(メンテナンス)をします。
また、1番目の引数は内部でテーブルオブジェクトを作成しているのですが、
そのオブジェクトの名称です。(別にテーブル名ではありませんが、同じ方が混乱は少ないと思います。)
後で、
var dbman:DBManager = DBManager.getInstance();
var sample1:Table = dbman.getTable("sample1");
にような感じで、オブジェクトを取得して、このテーブルオブジェクトのインスタンスに対して、
INSERTや、UPDATE,SELECTなどをしていくことになります。
ちなみに、xml/sample1.xmlの内容は以下のようになっており、このファイルはソース上のパスとなっています。
< ?xml version="1.0" encoding="UTF-8" ?> <table name="sample" version="2"> <field name="sysid" type="integer" primary="true" auto_increment="true" /> <field name="str1" type="text" required="true" /> <field name="str2" type="text" /> <field name="num1" type="integer" required="true" /> <field name="num2" type="integer" default="0" /> <field name="num3" type="integer" since="2" /> </table>
このXMLファイルを見れば何となく、雰囲気はわかると思いますが、
ここでの特徴は、table@version という属性と、num3というフィールドにあるfield@since という属性です。
このファイルは、versionが1だったときには、num3というフィールドはなく、versionが2の時に、num3というフィールドが追加されたということです。
つまり、
dbman.registerTable("sample1","xml/sample1.xml");
という記述は、versionがアップされていることを検知したら、since属性を見て、
必要なフィールドをALTER TALBEして、フィールドを追加するようになっています。
また、NOT NULL制約などは、ALTER TABLEでは追加できないようです。(これはSQLiteの制約です。)
AIRで、DBを扱う際に面倒だな-と思っていたことをライブラリにしてちょこちょことまとめていたのですが、
使い方を含めてこのブログ上で、すこしずつ情報を公開することにしました。
まず、このライブラリは私がAIRの為に作成している共通ライブラリのDB関連の機能になります。
ソースはこちらで管理しています。
ちょっと、癖がありますが、私がGravity Mail(AIR版メーラ)で使っているライブラリとなっており、自分なりにクライアントに組み込みする為のDBライブラリとして作成することを心がけました。
その特徴として、
- テーブルのスキーマはXMLで管理。
- 非同期モードで動作することが前提。
- テーブルに対する追加・更新・削除などをイベントとして取得可能
(もちろん、このライブラリを通しての処理の限りますが・・・) - ListCollectionViewなどのインターフェースももって、DataProviderにもなることができる
となっています。
テーブルのスキーマはXMLで管理
このライブラリの特徴でもあるのですが、テーブル定義はXMLで行います。
イメージとしては、昔JavaであったTorqueというライブラリをこのあたりは参考にしています。
このようなXMLの管理をしている理由はいくつかありますが、大きく言えば下記のような理由です。
- 自動的にDDL文などを作成したため。
- Mapperとして利用する際に、各フィールドのメタ情報を管理したいため
この情報により、INSERT文なども自動的に作成します。 - テーブル構造のバージョン管理もしたいため
XMLのバージョンと実際に作られているテーブルを見て自動的にカラムを追加するなどの機能を持ちます。
ただし、バージョンダウンはありません。 - ドキュメント等をXSLTなどで自動で作りたいため
です。
このDDL関連のイメージは、こちらでみることができます。
非同期モードで動作することが前提
このあたりは結構、悩んだのですが、やはり画面関連の操作を考えると、
非同期モードの方がよいと思い、そのような前提としました。
ただし、なるべく面倒がないようにしているつもりではいます。
テーブルに対する追加・更新・削除などをイベントとして取得可能
たとえば、一覧画面から1レコードを選択して、情報を更新した際に、
自動的に一覧の情報は更新されて欲しいはずです。
このような用途の為に、テーブルの情報を変更した際にイベントを発行するようにしてあります。
イベントを発行、そして、取得する為にはこのライブラリを通してデータの変更をすることが必要です。
たとえば、数(レコード数)が少ないマスター系などはこのイベントを使って、内部キャッシュ情報として保持しておけば、
SQLでもJOINを使わず、そして、同期型関数としてデータを取得できるはずです。
このような手法を使って、非同期モードの使いにくさをカバーしてもらえたらと思います。
ListCollectionViewなどのインターフェースももって、DataProviderにもなることができる
このあたりは、UIを意識した形になっています。
テーブルオブジェクトを、引数に渡すだけでDataGridや、Listとして表示することができます。
上のテーブルに対する追加・更新・削除のイベントを発行する理由は、
このUIの再表示をプログラマがいちいち記述しなくても、自動的に再表示しようという魂胆です。
ちなみに簡単な条件(WHERE文)は指定も可能ですので、
表示したい形にテーブルを作れば簡単にそれらしいアプリも作れるのではと思います。


