日曜日, 8月 31, 2008

【HTML5】 開発者の率直な意見をまとめてみた このエントリーを含むはてなブックマーク


HTML5 からの抜粋

何で今更HTMLなの?

訪れるのは輝ける未来か、はたまたHTML3.2の悪夢再来か。
次世代HTML規格HTML5について語りましょう。
で、何で今更 HTML なの?

XHTML でいいじゃない
みんなが標準標準言い出したのは、
XMLならソフトでも処理しやすいから、XHTMLに移っただけで、
タグ付けに意味を求めたわけじゃないだろう。
HTMLに戻られても・・・

HTMLなんだからもっとざっくりでいいんだよ
作るべきは正しい構造を示す言語じゃなくて誰でも情報発信できる道具にするべき

この世界は、結果的に性善説で発展してきたからなぁ。
もしブラウザが、W3CのValidatorに通らないような腐ったマークアップを、
パースエラーだして排除するような世界だったら、
今のように、もはやOSではなくてWebアプリの時代だ。とはならなかっただろうし。

実際さ、使われて見なきゃ、問題点がわからんよ。

誰かがXHTMLだからこそって言う画期的なものを作らないとHTML5への流れは変わらないんじゃないかな


なんとかXMLにしたい意見

いっそのこと、DOCTYPE宣言なしのXHTML文書にして、普段はXHTML 1.1のマークアップを行いながら、部分的にHTML 5のvideo要素を使ってみたり、XHTML 2のセマンティックな属性を使うのってありかな?

HTML5にもいいと思える部分もあるし両者のいいとこどりで、XHTML.1後継を作ってくれるのがいちばんいいんだが。

XHTML1にHTML5の追加要素がよい。


モジュール化に関する意見

what-wgが「HTML5はモジュールに分割せずモノリシックな規格を作る」って決めてしまったからなぁ…

<video src="video.ogv" xmlns:cc=" http://creativecommons.org/ns#"
rel="cc:license" resource=" http://creativecommons.org/licenses/by/2.1/jp/"/>
XHTML 5でこういうことができたらいいのになあ。video要素とRDFaを両方使うとオレオレXHTMLになってしまう。せっかくスキーマを取り払ったんだから、モジュール化して他のモジュールも自由に取り込めるような仕組みがいいと思う。

validatorにかけると
Error: Attribute rel not allowed on XHTML element video at this point.
などのエラーを返される。@relや@resourceをvideo要素に書くとHTML 5準拠にはならないんだよね。ブラウザでの表示やRDFの抽出はできるだろうから実際あまり問題はなさそうだけど、そのXHTML文書が準拠する仕様はどこにも存在しないってのは、ブラウザの独自拡張時代を思い出させてあまり好きではない。HTML 6あたりで「やっぱりCSSみたくモジュール化しよう」ってことになり、Multimedia ModuleとかMetainformation Attribute Moduleとかが定義されて、制作者はいろいろなモジュールの中から好きなものを選んで組み合わせて使えるようになったりしないかなあ。


属性ではなくタグにする意味とは

ブラウザの後方互換を考えれば、紳士規定でdivに名づけられたidとclassを認識するほうが便利だけどね。
要素増やすよりも指定してそっち拾ってもいい気はするし、むしろスマートな気もする。
だけど、肝心のdivが匿名要素だし、匿名要素と考えないとdivを引っこ抜いて考えることもできない。
最悪、ソフトがデーターを引っこ抜こうとしてidとclass被っていたら使えないんだし。

その辺は、仕様を決める側の方針によるんじゃないかな。XHTML 2はW3Cが目指してきたXMLベースの文書構造化言語の集大成的なやつで、今必要だからといって新しい要素を次々に導入してきたわけではない。HTML 5は最近のウェブページに共通して表われるサイドバーとかエントリとかを要素としてマークアップできるよう新要素を取り入れたりしていて、「その方針では際限がない」って批判は真っ当なものだと思う。便利そうに見えるのはHTML 5のわけだが。

問題はdivとspanじゃソフトが拾い上げられないことだ。
idとclassを紳士規定で足並みを揃えたら、要素が増えたこととほぼ同じなんだけど、
ブラウザ以外のソフトに読み込ませることを考えたら、要素の方が楽だろ。

<head>
title要素を含むページ情報
</head>
<body>
<header>
h1とかナビゲーションとか色々
</header>
<contents>
読み出したい文章
</contents>
<footer>
著者名とか連絡先とか
</footer>
</body>

現状だとbodyの中身とheadの中身を拾い上げられるが、肝心のcontentだけ拾い上げるのは無理。こればっかりは要素を増やすしかない。

HTML 5は名前変えたところで結局Web Applications 1.0だしそこに価値があると思うから
そっちの話がもっと出来たらと思うんだがなぁ
要素型の話っていってもテンプレート系の要素のことだったりをここで聞いたってほとんど無反応だろうし


完全互換実装の可能性

CSS3やXHTML2の例を見れば、2010年に出したいだけで、2022年になっても出ない気がする。

完全互換の実装が少なくとも2つ、そのためのテストがどんなに少なく見積もっても20000か。
・・・永遠に出ない気がしないでもない。


<関連記事>
 標準が生き残るのは学習コストが見合う場合に限る
HTMLなのかXMLなのか、それが問題だ
XMLシリアライゼーションの意味

土曜日, 8月 30, 2008

【HTML5】 HTMLなのかXMLなのか、それが問題だ このエントリーを含むはてなブックマーク


 HTMLとXMLの大きな違いは、要は、すべての開始タグと終了タグが対になっている(Well-Formed)かどうかである。Well-formedでなければパーサで処理できないためデータとして扱えなくなる。Well-formedでないHTML文書はスクレイピングを行って、構造化されたデータを引っ張り出してXMLに変換しなければならない。英語で"scrape"とは「削ること」。構造化されていない部分は機械的に削ることはできないため、意味のある部分を「人」が判断して例外があれば対応していくといった不毛な処理の積み重ねを行う必要がある。

 これまでWeb2.0の明るい技術として語られてきたセマンティックウェブは、Well-formedなXMLであることが前提とされる。RSS、ATOMやマイクロフォーマットなどによるメタデータ化、つまり、CGMを作成する側において、あらかじめ検索を行いやすい状態に加工を行っておき、データを検索用に整理しておくことが必要とされてきた。

 HTML5で作成されたコンテンツが実質的にWell-formedでなければ、Web世界の時計が大きく逆戻りすることになる。

 HTML5はWell-formedなのだろうか。もちろん、標準HTMLの場合<!doctype html>においてWell-formedでなければ意味がない。XHTML対応のためのXML構文もあるが、これはXHTML1.1の互換性を担保することで既存サイトを取込むことが目的と思われる。あるいは、上位はHTML5と認知させXHTML2への流れをくい止める目的があるかもしれない。いずれにしても、HTML5の本筋ではない。

 ちょっと話がずれるかもしれないが、XMLであればモジュール化も容易であった。

 HTML5はモジュール化しないの? 


 HTML5で新たに追加するとしている要素や機能は、HTML4を丸ごと再定義しなくても、こうしたモジュールの設計によって可能になるものが多いように見える。XではないHTMLのモジュール化はやりにくいかもしれないが、XHTML版V5をモジュールベースで定義した上で、そこからHTML版V5をつくることはできるだろう。


 モジュール化ができれば、HTMLの仕様がスマートになって綺麗になる。XMLであれば、こういったメリットも享受できるのだ。

 話は戻るが、もしHTML5がWell-formedであるのなら、私はXHTMLのことは忘れてHTML5を支持する覚悟はある。(私は手のひらを返す早さは誰にも負けない自信がある。)
 
 一方のXHTMLにおいても、XMLの特長を生かしきれない中途半端な実装が行われてきたことも事実である。例えばXMLの可読性はもっと強調されるべきであったが実際にはそうなっていない。XMLの要素は構造を表現するのに適しているし、XMLのタグを見ればどういう構造なのかをすぐに理解できる。ところが、XHTMLでは実際にはタグの要素名が使われずに属性が多用されてきた。何でもかんでもDIV要素のclass属性を使うのであれば別にXMLでなくてもいいじゃないかと思う。大部分のセマンティックウェブ技術もまたDIV要素のclass属性の多用であり、要素タグを追加することはあまりなかったと思う。それであれば、XHTMLを使う理由はなく、HTML5のDIV要素を使えばよいことになる。モュール化はできないかもしれないが、コンテンツ作成者から見たら実質的には関係ない。むしろ、単純化されるメリットの方が大きいかもしれない。

 以下の例を見ればわかるが、HTML5の方がセマンティックウェブとXMLが目指した理想に近い表現となっているのは、皮肉なことである。

XHTMLの文章構造


<body>
 <div id="header">...</div>
 <div id="nav">...</div>
 <div class="article">
  <div="section">
   ...
  </div>
 </div>
 <div id="aside">...</div>
 <div="footer">...</div>
</body>



HTML5の文章構造


<body>
 <header>...</header>
 <nav>...</nav>
 <article>
  <section>
   ...
  </section>
 </article>
 <aside>...</aside>
 <footer>...</footer>
</body>



<関連記事>
 開発者の率直な意見をまとめてみた
 標準が生き残るのは学習コストが見合う場合に限る
 セマンティックウェブにXMLはいらない?
XMLシリアライゼーションの意味
 

金曜日, 8月 29, 2008

【雑記】 AtomPubは学習コストに見合わない例 このエントリーを含むはてなブックマーク


 ちょうど今、はてなの情報をみてたら、はてなダイアリーAtomPubを公開しました(開発者向け情報)とあった。
 AtomPubも、あくまで個人的な意見ではあるが、コスト対効果という点では見合わないと判断した仕様である。冗長な部分は削り、定義すべきは明確に定義すべきであるが、今はお世辞にもそうなっていない。
 はてなの実装はシンプルなので評価できるが、そもそもAtomPubの仕様自体も、これぐらいで十分だったのではないかと思う。いい機会なので、AtomPubの不満を列挙してみる。

 1) Service、Workspaceの意味が曖昧で不十分。どうせなら再帰的な構造を許すべき。親子関係、すなわち、Workspaceの下にWorkspaceを持てる親子関係が作れるといいと思った。そうすれば、Workspaceをファイルシステムのディレクトリのような構造にできるのに。それから、URLのスラッシュで下の階層を示すとか、ServiceやWorkspaceとのマッピングについて、CoolURIを考慮した踏み込んだ定義が欲しかった。

 2) WSSEを標準と明言すればよかった。最もシンプルで実用的であるにもかかわらず正式に採用されていない。現状のサイトの実態を見て欲しい。WSSEのトークンの作成方法一つとっても様々な実装があって混乱しているというのに・・・。それをAtomPubがやらんで誰がやる?

 3)エントリーの抽象化が不十分。AtomのエントリーはCGMの一つと思わせるに至っていない。AtomPubはリソースの操作をエレガントにできてはじめて真価を発揮すると思う。抽象化ができず、Atom(Syndication Format)の操作のみであるなら、GDataAPIと互換性を持たせてくれた方が(クライアントを開発する側としては)ありがたい。AtomPubとAtom(Syndication Format)は、本当は別々の仕様なのにもったいない話である。

だいたいこんな感じかな。学習コストと効果という観点でいえば、現状のAtomPubを使うのはないだろうなと、あらためて思った次第である。

【HTML5】 標準が生き残るのは学習コストが見合う場合に限る このエントリーを含むはてなブックマーク



「今後はWeb標準に準拠してください」、マイクロソフト

 本当にマイクロソフトの言葉だろうか。かつて、IEとActiveXが、Windows Updateで「トロイの木馬」のようにユーザのPCに侵入してきて、それに気がつかないまま便利だから使っているうちに標準となっていく時代があった。(XMLHTTP通信はまさにそうだろう?) 
 かつてあれほどまでに独善的野望をもって執着してきた特権をマイクロソフト自身が捨てるような発言をするなんて、にわかに信じがたいものがある。でも、今はそういう時代ではないことが、HTML5の登場の背景を知ると理解できる。

HTML5が持つ本当の意味

 
<前略>こうした状況に業を煮やしたSafari、Moziila、OperaなどのWebブラウザベンダは、2004年6月にWHAT WGという団体を発足します。彼ら自身は同団体の発足声明の中で、WHAT WGを、いわゆる業界団体のようなものではなく、現場の個人個人が集まって構成した非公式な集まりだと説明しています。

 WHAT WGは、すでに説明した「Web Forms 2.0」のほかに、アプリケーション開発向け仕様の「Web Apps 1.0」、UI設計用の「Web Controls 1.0」のマークアップ言語・APIの策定を行います。Level0からLevel3まで、すべてのDOMの仕様策定に7年もかかったW3Cの遅すぎる標準化プロセスに対して批判的な人々というだけあって、仕様策定も実装もハイピッチで進みます。


 Canvasに代表されるような、IE非互換の仕様が多く支持されるようになったのは、FirefoxやSafariなどのブラウザの影響が大きい。これらは数のうえではまだIEには及ばないものの、性能面ではIEを圧倒するようになっている。今や主導権はこれらのWebブラウザベンダのものとなり、WHAT WGという形で仕様策定に参加してHTML5を作りあげてしまったのだから、マイクロソフトとしては、Web標準に準拠といわざるをえなくなったのだろう。

 まあ、それはいいとして、私自身、納得のいかない点がひとつある。それはXHTMLのトーンダウンである。XHTMLは、データの形式や構造をきちんと定義させることで、セマンティクス、すなわちHTMLのデータに意味をもたせるものとして、非常に重要な役割があると信じてきた。単なる文字データであったHTMLを、コンテンツの「すべての付随する利点を伴った XML の世界に参入できる」(by kanzaki web)。しかし、XML拡張による複雑性が災いしているのか、XHTMLによるセマンティックウェブ化は当初の思惑通りに進んでいないように見える。


2000年前後にバーナーズ・リーが描いた絵は大きいものでした。すなわち、HTMLをXMLへ移行させ、さらにXMLで定義された概念辞書などを使ってメタ情報を扱う。そうすることでウェブは単なる文字や画像の表示装置ではなく、意味論のレベルで有機的に構成されたシステムへと進化するというシナリオです。バーナーズ・リーは、この次世代ウェブを“セマンティックウェブ”と名付けて精力的に売り込みます。2001年には「サイエンティフィック・アメリカン」で取り上げられるなど、セマンティック・ウェブという名前は、一時バズワード的に広がりました。セマンティック・ウェブを実現するための関連仕様も、 RDF、OWL、GRDDLと、W3Cは次々とリリースしています。<中略>
XHTMLへの移行やセマンティック・ウェブの普及がなぜ起こらなかったかといえば、それはHTMLに比べて扱うのが難しすぎたからでしょう。
 記者の私見ですが、こうした例はHTMLとXHTMLに限らず、あちこちで起こっているように思えます。読むのも書くのも人間にとって負担の大きいXMLに対して、シンプルなYAMLが少しずつ広まってきているのが1つの例です。


 HTML5はセマンティック・ウェブの実現においても効果を発揮するという。しかし、それぞれのアプローチは明らかに異なるため、今後の仕様策定においても共通項を見出すことはないように思える。

XHTML 2とHTML 5はまだ別々の道を歩む

 HTML5はたしかに魅力的であり現時点で最も有力な標準仕様である。しかし、この路線で将来にわたっても発展していくかどうかはまだ何ともいえない。マイクロソフトをやり込めた後では新たなモチベーションが必要だろう。我々開発者は、この2つの仕様を見比べてみて、単純に新しい機能が豊富だからというだけではなく、互換性や生産性、将来性(方向性)、学習コストを考慮したうえで選択するのが最良の方法だと思う。とりわけ、学習コストが折り合うかどうかは重要である。せっかくのよい技術でも、もし折り合わなければ誰からも見向きされなくなり、そのうち忘れ去られていくことになる。今は盛り上がっているけれども、一時的なものではないのか?人には有限の時間しか与えられていないのだから、学習コストについてはよくよく考えるべきである。

<関連記事>
HTMLなのかXMLなのか、それが問題だ
 開発者の率直な意見をまとめてみた


水曜日, 8月 27, 2008

【Reflex】 ドメイン専用言語としてのReflex表現 このエントリーを含むはてなブックマーク



 何気にSeasar Conference 2008のエントリを見てたら興味ある見出しがあった。

B1: 「近」未来のプログラミング言語
Java 言語用フレームワークではアノテーションの活用が当たり前になりました。アノテーションによって、我々は XML 地獄からぬけだして、簡単にプログラミングできるようになりましたが、気づいたらそこはアノテーション地獄でした。天国への蜘蛛の糸は「ドメイン専用言語」といわれていますが、本当でしょうか?


 ドメイン専用(特化)言語はよく知らないので、もう少し調べてみる。

ドメイン特化言語(DSL)

(前略)・・言語の文法を定義し、コード生成機能を使ってDSLから汎用的な言語を生成する、あるいは、そのDSL用のインタプリタを書くことです。こういったことを簡単にするツールがUnixにはたくさん揃っています。こうしたDSLのことを私は「外部DSL」と呼んでいます。 XML設定ファイルも外部DSLのよく知られた形式の1つです。・・(中略)・・
私はいつもDSLを作成するのと似たようなことを、設計を肉付けする際に行っています――クラスやメソッドがDSLとなるように開発するのです。できることなら、そのとき使っている言語でこれをやりたいのですが、もしできないならできないで、コード生成を使うことになるでしょう。


 これはドメインを定義してモデルを自動生成することで生産性を上げようという話かな。私は、XMLの特性が生かせるのは、WSDLのようなAPI記述ではなく、ドメイン記述かもしれないと考えているので、基本的に賛成である。しかし、ドメイン記述に使ったのはいいけれども、その複雑さによりXML地獄をもたらしてしまった。そういう意味では、XMLSchemaもRelaxNGも同罪だ。

 しかし、JSONとRESTであればうまくいくように思える。

 複雑さはJSONおよびRESTfulに設計することで解消し、CoCによりDSLを作成するのと同じことができるようになる。具体的な私の解は、VTECメソッドを用いてRESTfulに設計し、4つのCRUD APIを実装する。そして、Reflex表現を使ってドメインクラスを自動生成することである。

<関連>
リソースの実装
自己完結したデータの集合
Reflex Entity Editorを公開

【Reflex】 きれいなXMLは好きですか?2 このエントリーを含むはてなブックマーク



きれいなXMLは好きですか

この記事は2年ぐらい前に書いたもの。ちょっと古いので書き直した。
今はRESTful設計ばかりを強調しているが、この頃はデシリアライザー機能を強調していた。
また、Reflex Entity Editorもなかったので追記した。

今後は分散機能が中心となるだろう。

火曜日, 8月 26, 2008

【Reflex】 Model設計における汎用的なフレームワーク このエントリーを含むはてなブックマーク



2003年頃のITProの記事

 Modelに相当する部分では,先進的な企業は何らかの取り組みをしているものの,そのノウハウは誰にでも利用可能な状態にはなっていない。Model は,プログラミングの領域の問題ではなく,オブジェクト指向による業務分析と設計を抜きにしては作れない。つまり,適用業務による差が大きい分野であり,汎用的なフレームワークを設計するのはそもそも難しいといえる。


 RESTfulに設計するとおのずとリソースの設計に重きを置かざるを得なくなる。Reflexではリソースの構造を共通のドメインとして定義し、リソースに対して4つAPI(CRUD)とすることでModelを汎用的に設計する。なんだかコロンブスの卵のような話である。

<関連> 
MVCモデルは進化する
APIをやめデータ構造に着目する

【Hadoop】 Hadoopの解析資料が公開されている このエントリーを含むはてなブックマーク



昨日、「Hadoop」解析資料がUpされていた。なかなかすばらしい資料だ。
オープンソース分散システム「Hadoop」解析資料
Hadoopの解析資料

オープンソース分散システムの利用検討は、大規模なデータ処理を低コストで実
現するための一つの手段として、企業にとっても重要な選択肢であると考えています。


 Hadoopはオープンソース分散システムである。オープンソース分散システムとクラウドコンピューティングは正確には違うもの(前者は必ずしもクラウドで動くとは限らない)のようである。Hadoopを人に説明するときに今まで何て呼んでいいか困っていたが、私もオープンソース分散システムということにしよう。

日曜日, 8月 24, 2008

【サービス志向】 Webサービスの課題、過去と現在 このエントリーを含むはてなブックマーク


 ティム・オライリーの論文『What Is Web 2.0』が2005年に発表されてから注目を集めるようになったWeb2.0であるが、それより遥か前にソフトウェアのサービス化について提言していたのは、実はOracleのLarry Ellisonである。
 1998年頃、NCA(Network Computing Architecture)の話に感銘を受けて、私もソフトウェアのサービス化の動向について言及したことがある。Webサービスが出始めた頃のことで、これがNCAを実現する最も重要な技術の一つだと思われたからだ。

Provison 2002 No.32「Webサービスによる非集中化アプリケーション」81P
Oracle社CEOのLarry Ellisonが「ソフトウェアはサービスに
なる」と宣言してから2年以上になりますが、サービス志向は
ASPという形で具体的な動きとなり、Webサービスの出現でさら
に加速する様相を見せています。
Webサービスでは、インターネット上の自己完結したビジネ
ス・モジュールをプラグ&プレイで実行できます。これは、ビジ
ネス・アプリケーションからビジネス・サービスへの変化、さらに
言えば、これまでのイントラネット中心の企業内アプリケーション
を開発・構築するためのソフトウェア体系から、インターネット
中心のB to B、B to B to Cなどの動的に他社のサービスを利用
するソフトウェア体系への大きな変化を意味します。


 この論文では、Webサービスの過去の課題、主にSOAP/UDDIのインターオペラビリティの課題について述べた。SOAPでもRPC型をやめてDocument型とするなど、当時もいろいろ工夫して解決してきた。そんなことをまとめたものであるが、インターオペラビリティについては、RESTとすることでほぼ解決できていると思われるので、今となってはあまり意味のない論文である。とはいえ、過去何をいってきたかを検証することは、未来を考えるうえでとても大切なことだと思うので敢えて振り返ってみた。
 
 今、私が思うWebサービス化のメリットは、第一に、システムの独立性を高められること、第二に、スケールアウト可能なシステムを作りやすいことの2点である。これらにより、並行開発が可能になり、スケーラビリティが確保できるようになる。また、SaaSを利用したり、クラウドと組み合わせることでコストメリットを出しやすい。

 逆に、課題としては信頼性の確保がある。信頼性とは、リスクをどう考えるかでも述べたが、システムそのものの信頼性向上、データの安全性確保の2点である。スケールアウトさせることでシステム管理対象が多くなるが、フォールトトレラントの仕組みを取り入れつつ、どうやって効率よく管理するかがカギとなる。一方、データの安全性確保はセキュリティ技術と運用方法がカギになる。

追記:プロフェッショナルとは、未来を予測するのではなく、未来を創っていくこと。 by 慶応大学 小池康博教授
 ただ予測できても意味がない。現在の課題を克服して実用性のあるものを創らなければ・・・。

土曜日, 8月 23, 2008

【Groovyコンファレンス】 プレゼン資料 このエントリーを含むはてなブックマーク

プレゼン資料をUpします。皆さん、お疲れ様でした!!

Groovyコンファレンス
View SlideShare presentation or Upload your own.

木曜日, 8月 21, 2008

【Amazon EC2】 クラウドコンピューティングに朗報 Amazon EBS このエントリーを含むはてなブックマーク


 先ほどAWSからEBSという新機能のアナウンスメールが届いた。アナウンス


 With Amazon EBS, storage volumes can be programmatically created, attached to Amazon EC2 instances, and if even more durability is desired, can be backed with a snapshot to the Amazon Simple Storage Service (Amazon S3).
 

 これはブロックストレージというものでEC2に動的にアタッチできるストレージ機能。これまでWebサービス経由でS3などに保存しなければならなかったEC2であるが、EBSによりパフォーマンスおよび可用性の向上が大きく図られることになる。

ネットを他に調べてみたら日本語のよい記事を発見した。

AmazonがEC2に「ブロック・ストレージ」を追加,RAID構成やスナップショットが可能

 あとは、Availability Zone 日本を待つだけかな。

日曜日, 8月 17, 2008

【Groovyコンファレンスデモ】 フローの実装 このエントリーを含むはてなブックマーク



 今回はフローの実装ということで主にsMashアプリの実装について説明する。
 sMashについては、最近、資料がUpされたようなので、詳細についてはこちらを参照していただきたい。

WebSphere sMash Announcement Workshop資料

 さて、sMashのアプリの説明に移る。今回やったことは以下の2つである。


1)JSONインスタンスをそのまま返すテスト用onListサービス
2)userid,monthパラメータを受け取り、iSeriesのリソースを選択し、検索結果をJSONPで返すonRetrieveサービス


とても単純なフローではあるが、当初の目的は十分に果たしている。ポイントは、パラメータに応じて動的にiSeriesのリソースを選択できていること。iSeriesがしっかりJSONで返すことができれば、後はsMash側で煮るなり焼くなりできる。iSeriesというレガシーシステムにアクセスするシステムで、これほどシンプルに特別な変換処理なく実現できることは驚きである。

 最後に、sMash(ZERO)のインストールなどを含めた今回のPMS Demoのインストールについて以下にまとめてあるので、あわせて参照いただきたい。Groovyコンファレンスデモの話はこれくらいでおしまい。 

pmsdemo


import zero.core.connection.Connection;
import zero.core.connection.Connection.Response;

def onList()
{
  // 初期表示&テスト用
  /*
  def  response = [ report : [ activitydetail : [
         ["07/05","10:00","18:00","JavaScript","暑い","80%"],
         ["07/06","10:00","18:00","CSS","寒い","20%"],
         ["07/07","10:00","18:00","HTML","普通","40%"],
         ["07/08","10:00","18:00","JSON","暖かい","70%"],
         ["07/09","10:00","18:00","Ajax","肌寒い","100%"],
         ["07/10","10:00","18:00","JavaScript","暑い","80%"],
         ["07/11","10:00","18:00","CSS","寒い","20%"],
         ["07/12","10:00","18:00","HTML","普通","40%"],
         ["07/13","10:00","18:00","JSON","暖かい","70%"],
         ["07/14","10:00","18:00","Ajax","肌寒い","100%"],
         ["07/15","10:00","18:00","JavaScript","暑い","80%"],
         ["07/16","10:00","18:00","CSS","寒い","20%"],
         ["07/17","10:00","18:00","HTML","普通","40%"],
         ["07/18","10:00","18:00","JSON","暖かい","70%"],
         ["07/19","10:00","18:00","Ajax","肌寒い","100%"],
         ["07/20","10:00","18:00","JavaScript","暑い","80%"],
         ["07/21","10:00","18:00","CSS","寒い","20%"],
         ["07/22","10:00","18:00","HTML","普通","40%"],
         ["07/23","10:00","18:00","JSON","暖かい","70%"],
         ["07/24","10:00","18:00","Ajax","肌寒い","100%"],
         ["07/25","10:00","18:00","JavaScript","暑い","80%"],
         ["07/26","10:00","18:00","CSS","寒い","20%"],
         ["07/27","10:00","18:00","HTML","普通","40%"],
         ["07/28","10:00","18:00","JSON","暖かい","70%"],
         ["07/29","10:00","18:00","Ajax","肌寒い","100%"]
         ],
      assessment   : ["月の評価",
               "よくできました",
                 "2週目評価",
                 "3週目評価",
                 "4週目評価"
                ],
      plan      : ["月のPlan","1週目plan","2週目plan","3週目plan","4週目泉岳寺にいきます"],
      task      : [
               ["週報システム","設計","クラス図","8/1","8/15","5"],
               ["週報システム","設計","クラス図","8/16","8/19","3"],
               ["取引システム","開発","Javaソース","8/20","8/31","2"]
                ],
      userid     : "user01",
      month     : "200808",
      responsecode  : "201",
      createdate   : "Tue Aug 05 16\\:04\\:44 JST 2008",
      ]
    ];
*/
    Connection.Response resp = Connection.doGET("http://localhost:8083/pmsdemo/jp/reflexworks/pms/model/Report?userid=user1&month=200808&json");
    def body = resp.getResponseBodyAsString();
    def respJSON = zero.json.Json.decode(body);

    // JSONで返す例
    print "var jsobj = ";
    request.json.output = respJSON;
    request.view ="JSON";
    render();
}

def onRetrieve()
{

  def userid = request.params.userid[];
  def month = request.params.month[];
  
  def server="";
  
  // ユーザに応じてアクセス先を変える
  if (userid=="user1") server = "http://localhost:8083/";
  if (userid=="user2") server = "http://localhost:8085/";
    
  // リソースにアクセスする
  def Connection.Response resp = Connection.doGET(server +"/pmsdemo/jp/reflexworks/pms/model/Report?json&userid="+userid+"&month="+month);
  
  // リソースをJSONにする
  def body = resp.getResponseBodyAsString();
  def respJSON = zero.json.Json.decode(body);

  // JSONPで返す
  print "callback(";
  request.json.output = respJSON;
  request.view ="JSON";
  render();
  print ");";
}




土曜日, 8月 16, 2008

【Reflex】 JSONからJavaに変換 このエントリーを含むはてなブックマーク



 これまでデモで説明してきた、JSON⇔Java変換は、Resource Mapperの機能の一つである。これは、Reflex Coreプロジェクトの中に含まれている。JSON⇔Java変換機能はいろいろあるようだが、ArrayやMapではなくて、Listとして受け取るのがミソ。Listの中に子要素を入れることで、何階層でも入れ子にできる。Mapなどに比べ直感的ではないが意外と扱いやすい。

Reflex JSON
Reflex JSON Sample

金曜日, 8月 15, 2008

【Groovyコンファレンスデモ】 リソースの実装 このエントリーを含むはてなブックマーク


 今回はリソースの実装である。具体的には次のような非常にシンプルなAPIを定義することになる。


<API例(entityはリソースの1インスタンス)>

entity = create(entity);
entity = retrieve(entity);
entity = update(entity);
entity = delete(entity);


 サブシステム内であれば上記APIをそのまま使う。サブシステム外ではHTTPのPOST/GET/PUT/DELETEがそれぞれ対応する。例えば、GETを実行するとシリアライズされたentityが返ってくる。以下が実行結果である。リソース実装では、このような結果を取得できることを目標とする。<参考>リソース志向

<JSON>

<XML>



 さて、ここでちょっと設計について補足しておきたい。この記事では以下のように説明した。


 アプリケーションの作り方であるが、具体的には次の順番で始める。この4番目まで作ったら、インスタンスを各レイヤに渡して後は個別での作業となる。(これについては次回以降説明する)
 1.画面のモックアップを作る
 2.モデリングを行い画面のEntity(Model)を作成する
 3.テーブルのEntityを作成する(DAO)
 4.Entityのインスタンスを作成する

(※)Reflex設計ではModelをEntityの構造として定義してドメインとしてシステム全体で使用する


 ここでいうモデリングとは、実は分析クラスを設計することを意味している。つまり、2.の画面のEntity(Model)を作成というのは、Boundaryの分析クラスを設計することと同じ意味である。通常は、Controlの分析クラス、Entityの分析クラスとそれぞれ設計していくのだが、Boundaryの分析クラスからEntityの分析クラスを導きだして、それを共通のドメインとして使おうというのが、Reflex設計における重要なポイントとなる。
 全体で行う設計は、Entityの分析クラスの構造を定義し、そのインスタンスを各レイヤに配るところまででおしまい。そのため、全体の設計で登場するのはModelではなくEnitityとなる。
 Modelは設計クラスであり分析クラスから導出するものだ。ViewやContolerも同様である。しかし、MVCモデルは進化するでも述べたが、今回は設計クラスのモデリングは行わない。
 テーブルの構造は、BoundaryとContorolでは関知しないので、3.テーブルのEntityを作成する(DAO)は、正しくはResourceレイヤの責務のなかで行う。またDAOは設計クラスなので、分析クラスのモデリングでは登場しない。

<MVCモデルは進化するの抜粋>

そこで、得た結論は、

否定の6.オブジェクト設計をしない


である。正確には、設計クラスのModelをオブジェクトにはしないで、オブジェクトで扱うデータに関心を集めようというもの。Modelとは、モデリングによってできた成果物(※)でありオブジェクトであるから、データとプログラムがくっついている状態。それからプログラムを切り離してデータのみに関心を集めて考える。データだけであればオブジェクトではないのでオブジェクト設計をしないことを意味する。とはいえ、分析クラスまでは設計して論理ビューを作る。論理ビューで定義したサブシステム内であれば、勝手に設計/実装して構わないという感じになる。
(※ Modelの定義は曖昧で様々な説明が存在する。私はこの説明が一番好きだ。)


 Reflex設計では、設計クラスのモデリングは行わないのでModelという言葉はなじまない。
一方、分析クラスで導出したEntityクラスは、システム全体でドメインとして使用するため、画面でもEntityという言葉が頻発することになる。画面でEntityはちょっと違和感があるので、Modelと呼ぶ方が自然かもしれないが、ここは敢えてEntityと呼ぶことで、システム全体のドメインであることを強調したい。各レイヤにおいては、勝手に設計クラスを作成しても構わない。

 次に、本題であるリソースの実装について説明する。ここからは各レイヤ内の閉じた話なので、設計クラスのModelという言葉が登場する。具体的には以下のように実装する。


1.EntityにControllerを実装してResource Modelを実装する
2.DAO Modelを実装する
3.DAO ModelとResource Modelの変換をBlogicで行う


1.EntityにControllerを実装してResource Modelを実装する

Reflex表現から自動生成したBeanは、項目とアクセサー(getter/setter)だけを実装したものであり、データを格納する器の機能しかもたない。Reflexでは、以下のように、ControllerであるReportCtlを継承することにより、CRUD操作が可能なModelとして扱うことができるようになっている。CRUD操作はそれぞれHTTPのPOST/GET/UPDATE/DELETEに対応する。また、トランザクション管理を行っており、要素の入れ子にも対応している。つまり、子要素でエラーとなった場合に、Exceptionをスローすることで、一番親のところ(Servlet)でRollback機能が働く仕組みが提供されている。コードに中に明示的なcommit/rollbackの記述は必要ない。

<Report model>

package jp.reflexworks.pms.model;

import jp.reflexworks.pms.controller.ReportCtl;

import java.util.List;

public class Report extends ReportCtl {

  public List activity;
  public List assessment;
  public List plan;
  public List task;
  public String userid;
  public String month;
  public String responsecode;
  public String createddate;

  public List getActivity() {
    return this.activity;
  }

  public void setActivity(List activity) {
    this.activity = activity;
  }

  public List getAssessment() {
    return this.assessment;
  }
・・・・
}

2.DAO Modelの実装

以下は、実際のDAO ModelであるActivityDetailと、テーブルにアクセスするSQLを記述した部分である。ちなみに、ActivityDetailは、テーブルの構造をReflex表現で定義して自動生成したクラスである。先ほどは画面から項目を整理してEntityを自動生成したが、今度はテーブルの項目からReflex表現を作ることで、DAO Modelを自動生成する。
 また、DAOでは、Jakarta CommonsのDBUtilを使って直接SQLを書くようにしており、敢えてO/Rマッパは使っていない。一見、O/Rマッパは便利そうに思えるが、逆にブラックボックスが仇となって痛い目に見ることもあるため、展開された内部のSQLは把握しておく必要がある。結局、SQLを避けて通れないのであれば、直接記述する方法が最も理にかなっていると考えた。ここは、基本に立ち返ってSQLを記述するのがベストである。とはいえ、SQLを直接記述しても、DBUtilであれば項目名とフィールド名を一致させておくことで、ResultSetからわざわざ詰め替えなくてもO/Rマッパの検索結果のように結果オブジェクトを得ることができる。これで十分である。

<DAO Model>

package jp.reflexworks.pms.model;

import jp.reflexworks.pms.controller.ActivitydetailCtl;

public class Activitydetail extends ActivitydetailCtl {

  public String userid;
  public String actiondate;
  public String timefrom;
  public String timeto;
  public String taskid;
  public String content;
  public String percent;

  public String getUserid() {
    return this.userid;
  }

  public void setUserid(String userid) {
    this.userid = userid;
  }

・・・
}

<DAO>

import java.sql.SQLException;
import java.util.List;

import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import jp.reflexworks.pms.model.Activitydetail;
import jp.sourceforge.reflex.dao.DBAccess;

public class ActivitydetailDao extends DBAccess {

  private static final String SELECT_LIST = "select" +
    " userid" +
    ",actiondate" +
    ",timefrom" +
    ",timeto" +
    ",taskid" +
    ",content" +
    ",percent" +
    " from activitydetail" +
    " where userid = ?" +
    " and substr(actiondate, 1, 6) = ?" +
    " order by actiondate";

  private static final String INSERT = "insert into activitydetail (" +
    " userid" +
    ",actiondate" +
    ",timefrom" +
    ",timeto" +
    ",taskid" +
    ",content" +
    ",percent" +
    " ) values (?,?,?,?,?,?,?)";

  public List getList(String userid, String month) throws SQLException {
    ResultSetHandler rsh = new BeanListHandler(Activitydetail.class);
    String[] params = new String[2];
    params[0] = userid;
    params[1] = month;
    return (List)query(SELECT_LIST, params, rsh);
  }

  public int create(Activitydetail activitydetail) throws SQLException {
    String[] params = new String[7];
    
    params[0] = activitydetail.getUserid();
    params[1] = activitydetail.getActiondate();
    params[2] = activitydetail.getTimefrom();
    params[3] = activitydetail.getTimeto();
    params[4] = activitydetail.getTaskid();
    params[5] = activitydetail.getContent();
    params[6] = activitydetail.getPercent();
    
    return update(INSERT, params);
  }
  ・・・


3.DAO ModelとResource Modelの変換をBlogicで行う

Blogicの役割はEntityの変換である。単なる変換もあれば計算などの処理が含まれる場合もある。(以下の定義を参照)
なぜこうしているかについては、「モデルの設計」に詳しく書いている。

1.entity = blogic(entity)
2.Blogicは揮発性である(ステートレスでありデータを保持しない。また、外部リソースへのI/Oはない)
3.Model内からのみ使用できるメソッドである(private)


 以下はReportCtlのソースである。ここで実際に行っているのは、DAO ModelとResource Modelの変換である。
 ReportCtlは、ReflexフレームワークのResourceOperatorを継承しており、onRetrieve()をoverrideしている。onRetrieve()では、DAO Modelであるactivitydetails, activitysummariesを使ってDBから検索し、ReportBlogicによりResource ModelであるReportに変換している。


package jp.reflexworks.pms.controller;

import jp.reflexworks.pms.blogic.ReportBlogic;
import jp.reflexworks.pms.model.Activitydetail;
import jp.reflexworks.pms.model.ActivitydetailList;
import jp.reflexworks.pms.model.Activitysummary;
import jp.reflexworks.pms.model.ActivitysummaryList;
import jp.reflexworks.pms.model.Report;
import jp.reflexworks.pms.model.Taskinfo;
import jp.sourceforge.reflex.controller.ResourceOperator;


public class ReportCtl extends ResourceOperator {

private ResourceOperator activitydetail;
private ResourceOperator activitysummary;
private ResourceOperator taskinfo;
private ReportBlogic blogic = new ReportBlogic();

/*
* 初期処理
*/
public void init() throws Exception {
activitydetail = factory.get(Activitydetail.class);
activitysummary = factory.get(Activitysummary.class);
taskinfo = factory.get(Taskinfo.class); }

/*
* 検索処理
*/
public Object onRetrieve() throws Exception {
// ActivitydetailのonRetrieve呼び出し
ActivitydetailList activitydetails = (ActivitydetailList)activitydetail.onRetrieve();

// ActivitysummaryのonRetrieve呼び出し
ActivitysummaryList activitysummaries = (ActivitysummaryList)activitysummary.onRetrieve();

// Reportクラスに変換
Report report = blogic.convert(activitydetails, activitysummaries);


return report;
}
・・・


 登録におけるonCreate()ではReportBlogic()を使って、入力であるReportオブジェクトの中のacivityからactivitydetaisに変換を行っている。(以下の太字)


package jp.reflexworks.pms.controller;

import java.util.List;
import java.util.Date;

import jp.reflexworks.pms.blogic.ReportBlogic;
import jp.reflexworks.pms.dao.ActivitydetailDao;
import jp.reflexworks.pms.model.Activitydetail;
import jp.reflexworks.pms.model.ActivitydetailList;
import jp.reflexworks.pms.model.Report;
import jp.sourceforge.reflex.controller.ResourceOperator;
import jp.sourceforge.reflex.util.HttpStatus;

import jp.sourceforge.reflex.exception.InvokeException;

public class ActivitydetailCtl extends ResourceOperator {

  private ReportBlogic blogic = new ReportBlogic();

  ・・・  
  /*
   * 登録処理
   */
  public Object onCreate() throws Exception {
    ActivitydetailDao dao = (ActivitydetailDao)daoFactory.create(ActivitydetailDao.class);
    Object entities = this.getEntities();
    ActivitydetailList activitydetais = null;
    int cnt = 0;

    if (entities instanceof Report) {
      Report report = (Report)entities;
      if (report != null) {
        activitydetais = blogic.getActivitydetailList(report);
      }
    } else if (entities instanceof ActivitydetailList) {
      activitydetais = (ActivitydetailList)entities;
    }
      
    if (activitydetais != null) {
      List activitydetailList = activitydetais.getActivitydetailList();
      if (activitydetailList != null) {
        for (int i = 0; i < activitydetailList.size(); i++) {
          Activitydetail activitydetail = activitydetailList.get(i);
          cnt += dao.create(activitydetail);
        }
      }

    } else if (entities instanceof Activitydetail) {
      Activitydetail activitydetail = (Activitydetail)entities;
      cnt += dao.create(activitydetail);

    } else {
      throw new InvokeException(HttpStatus.SC_NOT_ACCEPTABLE);
    }
    
    Date now = new Date();
    
    Report ret = new Report();
    ret.setResponsecode(HttpStatus.SC_CREATED);
    ret.setCreateddate(now.toString());

    return ret;
  }
・・


blogicのソースは以下のとおり。


package jp.reflexworks.pms.blogic;

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Calendar;
import java.util.Date;
import java.text.SimpleDateFormat;

import jp.reflexworks.pms.model.Activity;
import jp.reflexworks.pms.model.Activitydetail;
import jp.reflexworks.pms.model.ActivitydetailList;
import jp.reflexworks.pms.model.Activitysummary;
import jp.reflexworks.pms.model.ActivitysummaryList;
import jp.reflexworks.pms.model.Assessment;
import jp.reflexworks.pms.model.Plan;
import jp.reflexworks.pms.model.Report;
import jp.reflexworks.pms.model.Task;
import jp.reflexworks.pms.model.Taskinfo;
import jp.reflexworks.pms.model.TaskinfoList;

public class ReportBlogic {

  public Report convert(ActivitydetailList activitydetails, ActivitysummaryList activitysummaries) {
    Report report = new Report();
    
    Date now = new Date();
    SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
    String createddate = format.format(now);
    
    List activityList = new ArrayList();
    List assessmentList = new ArrayList();
    List planList = new ArrayList();
    List taskList = new ArrayList();
    
    // activity
    if (activitydetails != null && activitydetails.getActivitydetailList() != null) {
      for (int i = 0; i < activitydetails.getActivitydetailList().size(); i++) {
        Activitydetail activitydetail = activitydetails.getActivitydetailList().get(i);

        Activity activity0 = new Activity();
        Activity activity1 = new Activity();
        Activity activity2 = new Activity();
        Activity activity3 = new Activity();
        Activity activity4 = new Activity();
        Activity activity5 = new Activity();
        
        activity0._$$text = editMD(activitydetail.getActiondate());
        activity1._$$text = editHM(activitydetail.getTimefrom());
        activity2._$$text = editHM(activitydetail.getTimeto());
        activity3._$$text = activitydetail.getTaskid();
        activity4._$$text = activitydetail.getContent();
        activity5._$$text = editPercent(activitydetail.getPercent());
        
        activityList.add(activity0);
        activityList.add(activity1);
        activityList.add(activity2);
        activityList.add(activity3);
        activityList.add(activity4);
        activityList.add(activity5);
      }
    }
    
    if (activitysummaries != null && activitysummaries.getActivitysummaryList() != null) {
      String breakTaskid = "";
      for (int i = 0; i < activitysummaries.getActivitysummaryList().size(); i++) {
        Activitysummary activitysummary = activitysummaries.getActivitysummaryList().get(i);

        // assessment
        Assessment accessment = new Assessment();
        accessment._$$text = activitysummary.getAssessment();
        assessmentList.add(accessment);
        
        // plan
        Plan plan = new Plan();
        plan._$$text = activitysummary.getPlan();
        planList.add(plan);
        
        // task
        if (!breakTaskid.equals(activitysummary.getTaskid())) {
          Task task0 = new Task();
          Task task1 = new Task();
          Task task2 = new Task();
          Task task3 = new Task();
          Task task4 = new Task();
          Task task5 = new Task();
          
          task0._$$text = activitysummary.getTaskid();
          task1._$$text = activitysummary.getDescription();
          task2._$$text = activitysummary.getOutput();
          task3._$$text = editMD(activitysummary.getTaskfrom());
          task4._$$text = editMD(activitysummary.getTaskto());
          task5._$$text = activitysummary.getMm();
  
          taskList.add(task0);
          taskList.add(task1);
          taskList.add(task2);
          taskList.add(task3);
          taskList.add(task4);
          taskList.add(task5);
          
          breakTaskid = new String(activitysummary.getTaskid());
        }
      }
    }
    
    report.setActivity(activityList);
    report.setAssessment(assessmentList);
    report.setPlan(planList);
    report.setTask(taskList);
    report.setCreateddate(createddate);
    
    return report;
  }
  
  public ActivitydetailList getActivitydetailList(Report report) {
    if (report == null) {
      return null;
    }
    
    ActivitydetailList activitydetailList = null;
    List activityList = report.getActivity();
    if (activityList != null) {
      activitydetailList = new ActivitydetailList();
      List activitydetails = new ArrayList();
      
      Iterator it = activityList.iterator();
      while (it.hasNext()) {
        Activitydetail activitydetail = new Activitydetail();
        activitydetail.setUserid(report.getUserid());  // userid
        
        if (it.hasNext()) {
          Activity activity0 = it.next();
          activitydetail.setActiondate(editDbYMD(report.getMonth(), activity0._$$text));  // actiondate
        }

        if (it.hasNext()) {
          Activity activity1 = it.next();
          activitydetail.setTimefrom(editDbHMD(activity1._$$text));  // timefrom
        }

        if (it.hasNext()) {
          Activity activity2 = it.next();
          activitydetail.setTimeto(editDbHMD(activity2._$$text));  // timeto
        }

        if (it.hasNext()) {
          Activity activity3 = it.next();
          activitydetail.setTaskid(activity3._$$text);  // taskid
        }

        if (it.hasNext()) {
          Activity activity4 = it.next();
          activitydetail.setContent(activity4._$$text);  // content
        }

        if (it.hasNext()) {
          Activity activity5 = it.next();
          activitydetail.setPercent(trimPercent(activity5._$$text));  // percent
        }
        
        activitydetails.add(activitydetail);
      }
      activitydetailList.setActivitydetailList(activitydetails);
    }
    
    return activitydetailList;
  }

・・・



【Groovyコンファレンスデモ】 ビューの実装 このエントリーを含むはてなブックマーク


 今回はビューの実装の話。ビューとは、BCEでいうところのBoundaryで、今回のデモではブラウザのAJAXアプリケーションを指す。(参考=>各レイヤと実装技術

1.ビューの実装


 基本的にAJAXで動作するアプリケーションということで以下の方針で実装する


1.サーバから取得したJSONを元にJavaScriptで画面をレンダリングする。JSONPを使う。
2.画面に入力したデータをJSONにシリアライズしてサーバにPOSTする。AJAXを使う。


 これはサーバ通信が前提のアプリケーションではあるが、実際には統合テストにおいてサーバ通信ができればよいので、それまでは前回の記事4.で作成したインスタンスを元に、クライアント環境だけで開発していく。画面のレンダリングなどがきちんと作成できた後で通信部分を実装すればよい。なお、sMashではAJAX通信のためのDojoベースのライブラリを用意しているが、今回は使用していない。





 クライアント環境だけで動作するアプリケーションは、自身にインスタンスを埋め込んだもので、このような感じのものになる。

サーバ通信しないPMSアプリケーション

木曜日, 8月 14, 2008

【Groovyコンファレンスデモ】 ZEROからRESTfulアプリを作成 このエントリーを含むはてなブックマーク


 今回から数回にわたって、Groovyコンファレンスでデモする予定のアプリケーションPMSの仕組みと作り方について説明する。これはデモ用に作成した「日報入力アプリケーション」で、実際の業務で使うものではないが、基本は提案のソリューションでも同じ仕組みである。また、提案は携帯端末であったが、作り方に焦点をあてて説明したいので、普通のWebアプリケーションであることをご容赦願いたい。ただし、System-iへのデータアクセスは実際に行えるものを作る。<参考> PMS Demo

まず、開発のポイントをおさらいしよう。この記事の<提案>の部分である


 1.代表となるWebサーバ(マッシュアップサーバ)をインターネットに公開する
 2.各拠点のサーバ間はクローズドなVPN内で接続する
 3.マッシュアップサーバから各拠点へのアクセス経路をつくり、拠点の情報はマッシュアップサーバ経由で検索できるようにする。
 4.どの拠点にアクセスするかはマッシュアップサーバで判断する
 5.拠点のサーバはXML(JSON)でデータを返すだけ。検索結果の編集はマッシュアップサーバで行う


 上記を実現するため、BCEのそれぞれのレイヤにおいて以下の実装技術を使用する。


 1.ブラウザからはAJAXでアクセスする
 2.マッシュアップサーバにはsMashを使う
 3.拠点サーバ(System-i)にReflexを導入する




 この組み合わせのメリットは、前記事でも説明したが、それぞれの長所を生かせることと、BCEの各レイヤにおいて開発分業が可能になるという2点だ。

 さて、アプリケーションの作り方であるが、具体的には次の順番で始める。この4番目まで作ったら、インスタンスを各レイヤに渡して後は個別での作業となる。(これについては次回以降説明する)


 1.画面のモックアップを作る
 2.モデリングを行い画面のEntity(Model)を作成する
 3.テーブルのEntityを作成する(DAO)
 4.Entityのインスタンスを作成する

(※)Reflex設計ではModelをEntityの構造として定義してドメインとしてシステム全体で使用する(補足:リソースの設計







1.画面のモックアップを作る

 まずは頭を空っぽにして画面のモックアップを作成するところから初める。モックアップはHTMLとJavaScriptだけで作成する。(Groovy、Javaは使わない)

<週報画面>


<月報画面>



上図2つはそれぞれ異なる画面であり、タブでブラウザ内を移動できるようになっている。


2.モデリングを行い画面のEntityを作成する

 画面ができたら次にモデリングを行う。画面の項目について大、中、小と整理して構造を組み立てる。例えば週報の画面についてモデリングを行うと次のようになる。


週報
 計画
 実績
  日付
  出社時間
  退社時間
  タスク
  内容
  進捗%
 評価
 ユーザ
  ユーザID
  担当
  パスワード

 これを、Reflex Entity Editorを使って定義すると下図のようになる。




 ここで、上記のReflex表現から、Reflex Entity Generatorを使ってEntity Beanを生成する。以下は自動生成したBeanの一部である、Reportのソースだ。


package jp.reflexworks.pms.model;

import jp.reflexworks.pms.controller.ReportCtl;

import java.util.List;

public class Report extends ReportCtl {

  public List activity;
  public List assessment;
  public List plan;
  public List task;
  public String userid;
  public String month;
  public String responsecode;
  public String createddate;

  public List getActivity() {
    return this.activity;
  }

  public void setActivity(List activity) {
    this.activity = activity;
  }

  public List getAssessment() {
    return this.assessment;
  }
・・・・
}



package jp.reflexworks.pms.model;

public class Activity {

  public final int _$$col = 6;
  public String _$$text;

  public int get_$$col() {
    return this._$$col;
  }

  public String get_$$text() {
    return this._$$text;
  }

  public void set_$$text(String _$$text) {
    this._$$text = _$$text;
  }

}


 2番目のソースは、Reportのもう一つ下の階層である要素のActivityであるが、これは以下のような2次元配列を格納できるようになっている。$$col=6が配列内部の要素数を示している。


 def Activity = [["日付","出社時間","退社時間","タスク","内容","進捗%"]];



 JSONとJavaBeanの変換例とサンプルプログラムについては、こちらを参照=>Reflex JSON Sample

 3.テーブルのEntityを作成する
 

 ここで、具体的にテーブル設計を行って、必要であれば正規化を行う。
 先に定義したActivityは2次元配列であり、そのままではテーブルに格納できないため、Activityを変換して、テーブルにストアできるEnitityクラスを定義しなければならない。下図のActivityから引かれている矢印の先にあるActivitityDetailが変換すべきEntityとなる。
 いわゆる、O/Rマッピングをここで行うことになるのだが、一般的なWeb設計と異なり、テーブル設計がEntity設計を行った後になっていることがポイントである。それから、Entityと項目名を統一することでシステム全体の見通しをよくすることも重要である。
 既存のテーブルを使用する場合はしかたないとしても、テーブル設計を先にやってしまうと、リレーショナルモデルをシステム全体に及ぼすことになって不都合となることが多い。また、こちらに書いたように、オブジェクト設計の意味が半減してしまうのでなるべくなら後の方がよい。
 ちなみに、Entity Generatorは、Entityと同時にテーブル生成用のSQLも出力するので、そのままSQLを使用するのであればテーブル設計は必要ない。(この場合、O=Rのイメージとなる)
 
<O/Rマッピング>


4.Entityのインスタンスを作成する
 最後に、Entityのインスタンスを作成し、BCEそれぞれのレイヤの開発者に渡す。
開発者はインスタンスを元に開発することで相手システムと接続する必要がなくなる。その結果、相手の進捗によらず自分が担当している機能だけに集中できる。システムが疎結合であるため、このような開発スタイルが可能になったのだ。
<インスタンス>

var  report = {
activity : [
           ["07/05","10:00","18:00","JavaScript","暑い","80%"],
           ["07/06","10:00","18:00","CSS","寒い","20%"],
           ["07/07","10:00","18:00","HTML","普通","40%"],
           ["07/08","10:00","18:00","JSON","暖かい","70%"],
           ["07/09","10:00","18:00","Ajax","肌寒い","100%"],
           ["07/10","10:00","18:00","JavaScript","暑い","80%"],
           ["07/11","10:00","18:00","CSS","寒い","20%"],
           ["07/12","10:00","18:00","HTML","普通","40%"],
           ["07/13","10:00","18:00","JSON","暖かい","70%"],
           ["07/14","10:00","18:00","Ajax","肌寒い","100%"],
           ["07/15","10:00","18:00","JavaScript","暑い","80%"],
           ["07/16","10:00","18:00","CSS","寒い","20%"],
           ["07/17","10:00","18:00","HTML","普通","40%"],
           ["07/18","10:00","18:00","JSON","暖かい","70%"],
           ["07/19","10:00","18:00","Ajax","肌寒い","100%"],
           ["07/20","10:00","18:00","JavaScript","暑い","80%"],
           ["07/21","10:00","18:00","CSS","寒い","20%"],
           ["07/22","10:00","18:00","HTML","普通","40%"],
           ["07/23","10:00","18:00","JSON","暖かい","70%"],
           ["07/24","10:00","18:00","Ajax","肌寒い","100%"],
           ["07/25","10:00","18:00","JavaScript","暑い","80%"],
           ["07/26","10:00","18:00","CSS","寒い","20%"],
           ["07/27","10:00","18:00","HTML","普通","40%"],
           ["07/28","10:00","18:00","JSON","暖かい","70%"],
           ["07/29","10:00","18:00","Ajax","肌寒い","100%"]
           ],
assessment     : ["月の評価",
                   "よくできました",
           "2週目評価",
           "3週目評価",
           "4週目評価"
          ],
plan         : ["月のPlan","1週目plan","2週目plan","3週目plan","4週目泉岳寺"],
task         : [
              ["週報システム","設計","クラス図","8/1","8/15","5"],
              ["週報システム","設計","クラス図","8/16","8/19","3"],
              ["取引システム","開発","Javaソース","8/20","8/31","2"]
          ],
userid         : "user01",
month         : "200808"

};

水曜日, 8月 13, 2008

【sMash】 なぜReflexとsMashの組み合わせがよいのか このエントリーを含むはてなブックマーク


 今回は、先日の記事、ReflexとsMashによるソリューションの提案 の構成をなぜ考えたかについて説明する。最初はあまり深く考えず、直感的に、両者を組み合わせることで強みと弱みをお互いに補完しあえるペアになればいいな(オグシオ?)、ぐらいに考えていたのだが、デモを作っているうちに本当にそうだと確信できるようになってきたので具体的に説明したい。





 まず、sMashについては、こちらで説明したとおり、Controlの役割に使うことで真価を発揮する、と私は信じている。Reflexについては、Entity設計を基本に考えていることは時折述べているとおりである。つまり、この絵にあるように、sMashはControlに重きを置き、reflexはresourceに重きを置いている。もちろん、sMashだけでアプリケーションを作ることは可能である。Reflexもしかりなのだが、要は組み合わせによる相乗効果を期待できて、かつ、それが単体で作るよりも生産性が高くなるかどうかが判断基準となる。相乗効果と思えるものは具体的には以下のとおりである。まず、Reflexから。


<Reflexのメリット>

1.System-iからデータをXML/JSON化して簡単に取り出せる(JDK1.4ベースでTomcat4.1が動作するSystem-iで稼動実績がある)
2.Reflexを使うことでPDFを簡単に出力できる
3.リソースの統一したデータ表現が使用できる
4.トランザクションを明示的にコードに記述しなくてもよい



 Reflexのメリットのうち、最も効果が大きいと考えているのが、リソースの統一したデータ表現が可能になることである。この資料のP80を見ていただきたい。リレーショナルモデルは表形式、リソースモデルは自由とある。このリソースモデルは自由というのが曲者で、複雑なリソースをRDBにどうマッピングすればいいか、いつも問題になる。AXISでもwsdl2Javaに限界があり、複雑な入れ子のスキーマに対応できていなかった。それがAXIS2になっても改善されなかったのは酷い話である。AXIS2のデータバインディング能力

sMashではこの問題についてイベントを複数実装することで解決しようとしている。(資料P81)。でも、こうやってしまうとリソースが多い場合にマッピングだけで多くの労力をつかってしまうはめになる。「MVCモデルは進化する」でも述べたが、O/Rマッピングにかかる工数はバカにできない。
 そこで、Reflexの登場となる。Reflexでは、WSDLの代わりにReflex表現を使って自動生成したJavaBeanを使用する。Reflex表現を使えば複雑な入れ子のJavaBeanを生成できる。そもそもこの問題に対応することがReflex開発の動機であったのでできて当然である。あとは、それぞれのJavaBeanをDBのResultSetとして使用すればよい。ちなみにReflex表現はスキーマ言語の一種で、Reflex Entity Editorを使えばビジュアルに作成できる。

Reflex Entity Editor

次に、sMash。

<sMashのメリット>

1.外部リソースのデータに簡単にアクセスできる。(Integration FablicによるConnectionとMediation)
2.リクエストに応じて適切な場所に処理を振り分けることができる。(GlobalContextとEvent Handler)
3.処理や画面を簡単に開発することができる(GroovyとGroovy template)
4.コンポーネントを簡単にアセンブルできる(FlowとFlowEditor)


 一方、sMashのメリットは4のFlowによるアセンブル機能である。Integration Fablicにより、外部リソースを抽象化し、コンポーネントとして扱うことができる。また同時に、イベントハンドラーによるコンポーネントの呼び出しを可能にしている。これにより、Flow定義を記述するだけで開発できるようになる。後は出力用のデータを変換(マッシュアップ)を行えばよい。
 Flowのビジネスロジックからの外だしは長年にわたって革命的であると言われつづけ、ワークフローエンジンも多数商品化されたが、BreakThroghはできなかった。sMashでここまで簡単にやられると受け入れられる可能性がかなり高くなったのではないかと感じている。この資料より引用(P29)





最後に、補足として以前書いた記事を紹介しておきたい。

マッシュアップはサーバでいいんですか。いいんですよ。からの引用


それから、sMashで最も重要だと思う点は、サーバサイド Mashupであり、BCEでいうところのControlに照準を当てているということ。Controlは、ビジネスロジックというと語弊があるが、ワークフローのロジック部分といえばしっくりくる。これまでワークフローとWebアプリは別物のようなもので、開発者の多くはイメージしにくいかと思う。また、この記事でも書いたように、ControlはこれまでModelに無理やり詰め込まれていて、あまり意識せずにWebを開発できた。これまで曖昧で意識もされなかったControlであるが、RESTfulな設計を推し進めることで、相対的にくっきりと浮かび上がってきた。resourceは自己完結した情報の集合として静的に扱われる一方、ControlはBoundaryの要求に応じて動的にresourceを編集する機能となる。このようにきれいに分けることで、いわゆる疎結合開発ができるようなる。従来のWeb設計開発では、画面、ビジネスロジック、データベースの3つに依存した密結合開発であったものを、 RESTful設計ではビジネスロジックを外だしにして画面も独立して開発できるようになるのだ。この結果、分業という大きなメリットを享受できるようになった。


【Reflex】 貴族になって楽をしよう このエントリーを含むはてなブックマーク



 XMLスキーマの話で、ボヘミアンと貴族の階級闘争ってご存知だろうか。XML 1.1を巡る「ボヘミアン」と「貴族」の階級闘争

 厳密な設計なしにXML文書を書いてもよいとするボヘミアンに対して、厳密な設計なしにXML文書を書いてはいけない、とする貴族。

 昔は、厳密な設計とは型の厳密な設計を意味しており、型を厳密に定義するかについては議論が分かれていた。いずれの勢力も、要素と構造を定義することだけは当然のようにしていたとは思う。

 現在では、そもそも「ボヘミアンと貴族」なんてもういわなくなったが、両者の違いを敢えて挙げるとすれば、データ表現にスキーマを用いて定義するかどうかだろう。つまり、設計なしにXML文書を書いてもよいとするボヘミアンに対して、設計なしにXML文書を書いてはいけないとする貴族、というのが今の構図だろうか。昔に比べてボヘミアンよりになっていてスキーマはあまり書かなくなってきた。

 私はずっとボヘミアンを支持してきたし、自分はボヘミアンの筆頭だと自任していた。
これまでも、重厚長大なものを生みつづける貴族(IBM)たちに刃向かい、お前はハマスか!と怒られつつ、軽薄短小の価値をうったえてきたつもりであった。

 しかし、今の時代を考えてみると、どうやら貴族の部類に入ってしまったようである。

 Reflexはスキーマを必要とする。必要最低限の設計は、要素の名前と構造を定義することだけであり、Reflexもそれで十分動作するが、スキーマを定義することには変わりない。そうしないと最大のメリットであるデータのポータビリティが失われてしまうのだ。Reflexのモデルでは、Entityのポータビリティは必要条件である。スキーマを定義することで、インターオペラビリティが高められ、ポータビリティが得られて各レイアで開発を分業できるようになる。

 ちなみにsMashはずるいことにボヘミアンである。

一例を挙げよう。

 def abcdlist = [“a”:1, ”b”:2, ”c”:3, ”d”:4]

というハッシュに対して、次のようなXMLを生成する。abcdlistに追加できる項目の数に上限はなく、ハッシュにおいて、「"e" : 5, "f" :6」が追加されても対応できるが、これをXMLで表現できるのは、以下のように、キーを属性にして動的に追加できるようにしているからだ。


<abcdlist>
<value key=“a”>1</value>
<value key=“b”>2</value>
<value key=“c”>3</value>
<value key=“d”>4</value>
</abcdlist>


Javaでこれを実装すると次のようになる。


 Map map= new java.util.HashMap();
 map.put(“a”,1);
 map.put(“b”,2);
 map.put(“c”,3);
 map.put(“d”,4);


ところが、これには超えられない壁、RDBが待ち構えている。
例えば、以下のようなSQLを実行する場合、カラムaは静的であることが前提である。

select d from abcdlist where a='1';

keyが動的であることはテーブルに動的にカラムを追加することを意味する。

さあ、どうする?

 通常は、テーブルを別けるか、テキストを丸々ストアしてアプリケーションでパースするかのどちらかの対応が必要と思われる。
 私なら後者の方法を取るが、RDBを蔑ろにしているようで、これは好きになれない。
いずれにしても、パフォーマンスに影響する大事なところなので、本当に動的項目追加が必要なのかどうか、もういちど設計全体を見直した方がいい。

一方のReflexでは、XMLは以下のようなる。


 <abcdlist>
 <a>1</a>
 <b>2</b>
 <c>3</c>
 <d>4</d>
 </abcdlist>


スキーマであるReflex表現はこうなる。


 abcdlist
  a&
  b&
  c&
  d&


Java実装部分は次のようになる。


 Abcdlist abcdlist = new Abcdlist();

 abcdlist.a = new A();
 abcdlist.a._$$text = "1";
 abcdlist.b = new B();
 abcdlist.b._$$text = "2";
 abcdlist.c = new C();
 abcdlist.c._$$text = "3";
 abcdlist.d = new D();
 abcdlist.d._$$text = "4";


 ポイントは<abcdlist>の子要素に<a>~<d>が静的に定義されているということ。<e>や<f>を追加するにはスキーマの変更が必要になる。
 繰り返しになるが、このように静的に定義することで、インターオペラビリティが高められ、ポータビリティが得られるようになる。

 ちなみに、Reflexは先ほどのXMLを動的に定義することもできる。属性key付のvalueのListで、*を指定することで0..∞の繰り返しとすればよい。これは先ほどのReflexを使わない例と同様にキーが動的なためテーブルとマッピングすることが困難になる。また、属性を定義しているためJSONと不整合を起こすというデメリットがある。


 abcdlist
  value@key*&


 Java実装部分はちょっと冗長になるが以下のようになる。


 Abcdlist abcdlist = new Abcdlist();

 abcdlist.value = new ArrayList();

 Value value = new Value();
 value._$key ="a";
 value._$$text ="1";

 abcdlist.value.add(value);

 Value value = new Value();
 value._$key ="b";
 value._$$text ="2";

 abcdlist.value.add(value);

 Value value = new Value();
 value._$key ="c";
 value._$$text ="3";

 abcdlist.value.add(value);

 Value value = new Value();
 value._$key ="d";
 value._$$text ="4";

 abcdlist.value.add(value);


 ボヘミアンはワーキングプアになりがちである。自由を享受することばかりに関心をもつと後でイタイ目にあう。

 貴族になりさえなれば楽をすることができる。
 貴族になるにはスキーマを受け入れて動的項目追加をあきらめるだけでよい。(と、悪魔のささやきをしてみる)

 この踏み絵を前にして足を出せずに躊躇しているボヘミアンはよくよく考えるべきである。

月曜日, 8月 11, 2008

【Reflex】 Reflex Entity Editorを公開 このエントリーを含むはてなブックマーク


この記事で紹介しているテキストをマインドマップに変換するツールにインスパイアされて作ったEntity Editor。まだFirefoxでしか動かないが、とりあえず公開することにした。Entityが重要といいながら作成ツールをいつまでも作らないのはまずいよね。使い方は以下のページを見てほしい。

Reflex Entity Editor

 Reflex表現は、スキーマ言語の一種である。Relaxの簡易表現に似ているがもっとシンプルにしている。XMLでもJSONでも使えるのが特長だが、それはReflexが両方の表現に対応しているからだ。実際に使うには、Reflex表現からJavaBeanを自動生成するツールである、Reflex Entity Generatorを実行させる必要がある。

 それから、Reflex概要を更新している。reflex overview 

今後はReflexとsMashアプリの作り方をゼロから解説していきたい。具体的には、8/22にGroovyコンファレンス@IBM 渋谷で行う予定のデモを題材にしたい。

土曜日, 8月 09, 2008

【雑記】 僕とTAKE-DOS、ちょっとだけJava批判 このエントリーを含むはてなブックマーク



唐突だがちょっとした自慢話につきあってほしい。以下は、MZという8ビットマシンのDISKシステムに関する話だ。

MZ-80B、B2、2000、2200、2500

そのうち、MZ-2000でいわゆる本当にフォーマットが違うのは、地獄の練習問題だけでした。これは、いわゆる400KBフォーマットで、1トラックあたり、1024バイトx5セクタです。
しかも、プロテクトとして一部だったか全トラックだったかに同じセクタ番号が存在して、一つCRCエラーだったと思います。

これ以外は、全部普通の16セクタフォーマットです。
つまり、FDとしても普通の1トラック16セクタ対応+83トラックくらいまで対応していれば、たいてい対応可能です。

(Oh!MZのTAKE-DOSが83トラックあたりまで使用している、MZ-80BF
(だっけ、)だとオーバートラックできなくて動作しないんじゃなかったかと思います。
そもそも、MZ-80BFは70トラックまでしか動作しないことになっており、SHARP BASICは70トラックまでしか使用しない)


 TAKE-DOSは25年前に私がアセンブラで作ったDISK OPERATING SYSTEMだ。(当時16才)こつこつと1年かけて作って、翌年、Oh!MZという雑誌に応募して'85/5号に掲載された。
 そんな昔からアセンブラ書いてたの?とびっくりする人もいるかもしれない。でも、ヲタぶりをカミングアウトすることになってしまうので、よく考えたら自慢にはならない話だな。><; 何やってたんだ?とあきれる人の方が多いだろう。

 実はここからが本題。前記事の言語のところで、信頼性の話をしたのだが、それより重要なのは生産性かもしれないということ。生産性については、アセンブラよりCなどのコンパイラ言語、CよりJavaなどのインタプリタ言語、Javaよりスクリプト言語という順番だと思うが、グローバル変数の扱いやトランザクションなどを考えると、相対的に不具合の発生しにくいのは、やはりJavaなんではないか、ということをいいたかった。
 TAKE-DOSは、わずか数十Kバイトのコードだが、Z80アセンブラだと1年かかった記憶がある。
なぜそんなにかかったかというと、メモリ管理を考慮したサブルーチン化(構造化)ができなかったからだ。アセンブラは全部がグローバル変数のようなものなので今から思えばそうなって当然という感じでもある。最近のスクリプト言語は、さすがにメモリーリークは起きないが、グローバル変数は簡単に扱えるので安易に多用しがちな初心者はドツボに陥りやすい。件名:なぜ「グローバル変数」を使っては、いけないのですか?
 Javaでは普通、グローバル変数は扱わない。そのようなコードを書くと先輩にものすごく怒られるか、後輩にバカにされるかのどちらかである。基本的にJavaは言語仕様がよくできていると思う。しかし問題だなと思うのは冗長なこと。大した処理ではないのに往々にしてソースが長くなりがちである。クロージャがなかったり、文字列や連想配列(ハッシュ)などを簡単に扱えないのも原因の一つではあるが、私が感じるのは、「標準化対応」という重しをプログラマーが常に意識させられるということ。Javaには標準が数多く存在するので、プログラマーはそれを忠実に守ろうとする。Javaプログラマーはまじめで優等生が多いので、無視してしまうと罪悪感を感じ、また劣等感さえ芽生えてくるのだろう。

 Rubyのまつもとさんは短いコードの優位性についてこう述べていた。
「1000行のコードをかいたあとで、「仕様変更です」となると、じゃあこのコードをなんとか修正して、という発想になる。それが、仮に5行で書けるなら、じゃあこれは捨てて新しく書き直そうと思える。その結果、より効率的で変なバグも出にくいプログラムが作れます。」
 私も同じ意見だ。


また、プログラム一つだけなのに、それを動かすのにJarが20~30必要で、サイズも十数Mになったりする。自分は数十行しか書いていないのに、いつのまにか十数Mってどういうことなんだ? それから、不必要なJarコードにクラスパスを通すことに何の違和感を感じない人が多いのも問題だ。それは一つの文化なんだろうが本当によくないので改めるべきだ!
 一度Seasarを使ったあるプロジェクトでどうしてもBugが取れないことがあった。自分の書いたコードは悪くなさそうだったので、dependしているJarの何かがおかしいだろうということで洗いざらい調査することになった。案の定、diconファイル(設定ファイルの一種)が含まれているJarが見つかって修正することで解決できたのだがこれに約3日も潰してしまった。このとき思ったのは、「Jarの中の誰も見ないところに入れるなんて、diconを肥溜めに隠すようなものじゃないか。」(下品で申し訳ない)
 そもそもSeasarはファイルゼロ、無設定を売りにしていたはずで、diconファイルがあるのはおかしい。diconファイルは設定ファイルというより、インジェクションのフローに関わる重要な部分である。むしろプログラムに近く、動作の一部を肩代わりしているものだと思うので、開発者は必然的に意識することになる。ということであれば、やはり設定ファイルではなくて、Java言語内で解決すべきところなんじゃなかろうか。当初は敢えてJava言語外に定義することで、開発者に意識させないつもりだったのだろう。しかし実質的に大きく関係しているため、開発者にdiconを意識させなければならない結果となった。むしろ関係をよく知らない開発者は、先ほどのBugは3日でも解決できなかったかもしれない。「diconはプログラムとは別ものであり設定ファイルとは考えたくないのでJarファイルに入れた。プログラマはプログラムだけに集中すればよい。」<=これは私に言わせれば、臭いものに蓋をしているにすぎない。Seasarに限らない話だが、DIでは、一見関係なさそうなところが実は関係しているということがよくある。中途半端な依存関係が原因で起こる不具合の対応は恐ろしく時間がかかり難しい。場合によっては肥溜めまで調査しなければならなくなる。まるで探偵が推理をやるように、事件(Exception)から容疑者(Class)を見つけ出さなければならない。jarを含めた森羅万象をすべて把握している神のような者か、私のような何事も信じない性格の悪い人間でないと務まらないだろう。

 それから、アノテーションの多用で、意味不明のコードが多く見られるようになったことも問題だ。Javaを進化させるつもりが言語仕様を歪め、まるで癌のように蝕んでいる。アノテーションは基本的に廃止すべきである。言語仕様に限界があるのなら、小手先のアノテーションじゃなくて、いっそのことGroovyのようにプリコンパイラにすべきだと思う。Javaはグローバル変数の扱いや進化したトランザクション管理などもあって信頼性を高くしたが、逆に冗長性やアノテーションによって綻びはじめているのが現状といったところだろう。

 画面についてはもう終わっているので言及しない。Strutsを改善して生産性を上げようなんて無駄なことは考えない方がいいし、そもそも、Javaで画面を作るべきではない。

<ちょっとだけJava批判:まとめ>

  1.プログラムは努めて短くすべき
  2.Jarファイルへの依存を少なくすべき
  3.依存を完全把握できないのならDIは使うべきではない
  4.アノテーションをなくすべき
  5.画面は作るべきではない


1.3.の解決策として、私はgroovyがあると思う。DIが生産性向上を目的とするならgroovyで十分。あるいは、J2EEコンテナ代わりというのであればReflexで十分だ。依存関係をなくして完全分業したいのであれば設計モデルを含んだ話になる。私は既存の設計モデルではできないと思うので、RESTful SOAによる設計モデルが必要と考えている。いずれにしても、DIはいらない。
2.への対応はプログラマーの意識改革が必要だろう。
それから、5.は主にStruts、JSPのことをいっている。実は今sMashを使っていて、groovyのちょっとした画面編集ができるのだが、これは大変気に入っている。groovyはJavaであってJavaではない。話せば長くなるので別の機会に説明したい。

ところで、「TAKE-DOS MZ」というキーワードでGoogleを検索すると、こんなの=>MZ-2000 & FDD が出てきた。TAKE-DOS付きのMZ-2000、最高に欲しい!ちなみに、C-DOSというのは、CarryLabの平野さん(現インフォテリア社長)作のOS。ずいぶん前になるが平野さんと話す機会があって当時のことで盛り上がったのを覚えている。TAKE-DOSのことはご存知だった。

火曜日, 8月 05, 2008

【雑記】 Javaが本領を発揮する場面とは このエントリーを含むはてなブックマーク



最も初心者向けの言語はJava or PHP?の最後に、Javaの本領発揮する場面は後日述べたいと書いていて、その後トランザクション処理のところでも書いたつもりだったのだが、ぜんぜんJavaの本領発揮になっていないので、ここであらためて説明したい。

 Javaのいい点は信頼性である。Linuxは信じていないが、JavaのアプリケーションサーバであればLinuxでもOKという人は世の中に大勢いる。これはものすごく大きな価値であると思う。

 GoogleのPythonでMapReduceをやるのはまだ時期尚早であろう。それは理屈ではなく、基幹システムをLAMPで構築したときの苦労、私の経験がそう思わす。なので、HadoopやAmazon EC2、S3がJavaで書かれていることに素直に喜べる。利用者も安心してサービスを使うことができると思えるからだ。

 私はC、C+、VB、Java、JavaScript、PHP、Perlのほか、S390やZ80アセンブラ、COBOLを読み書きできる。Objective-Cをやれといわれたら書く自信はある。Javaは他の言語に比べると商売上得意なだけで、ひいきめにみているわけではない。要するになんでもいい。ただし、データのスキーマにはこだわる。スキーマは言語ではない。XMLでもJSONでもYAMLでも何でもいいがデータ構造がシステムで統一されていることが重要。これについては、APIをやめデータ構造に着目するエンティティの設計で詳しく説明した。

 私は基幹システムのクリティカルな部分にスクリプト言語を使う勇気は(もう)ない。一方で、Javaで画面を作ることはやりたくない。とてつもなく生産性が低いからだ。
 最近、若者のあいだで「Fuck! Java」とよくいわれている。何で?と訊くと嫌いだからという。いろいろやってみた結果ではなく、それがいまどきでカッコいいかららしい。何事も毛嫌いせず、向き不向きをよく理解したうえで発言してもらいたいものだ。

【クラウドコンピューティング】 景気が悪くなるからこそクラウド このエントリーを含むはてなブックマーク



不景気マーケットのキーワード ~中小ソフトハウスが知っておくべきこと~


これから景気は悪くなります。周期性の観点からも2011年、12年辺りまで、景気は浮揚しません。<中略>
新規投資は凍結し、運用・保守もより品質が高く安価なサービスへと興味の対象が移ります。実はそうした不景気マーケットに対して適価なサービスを提供できる企業が、正しく中小企業なのです。大手は価格が下がると固定費を賄う事が困難になります。そこで海外も含めて、より効率的にビジネスが展開できる場所を探します。国内は縮小し、なおかつ不景気による減価圧縮がキーワードとなります。業務効率や生産性の向上、現状支払いコストの圧縮というものがキーワードです。


 なかなかいいことをいうなあ。でも、「海外も含めて、より効率的にビジネスが展開」については、一点だけいわせてもらいたい。
 いわゆるオフショア開発についてだが。これは決して効率的とは限らない。例えばベトナムの場合、単価で計算すると約1/5だが、管理コストを含めた実際の成果物にかけたコストを計算すると我々日本人の方が安くなる場合がある。ただし、これには条件があって、コミュニケーション能力を含む高いスキルがあり、生産性の高い仕事を期待できるエンジニアに依頼できた場合に限る。同じ日本人でも彼らの生産性は10倍ぐらい違う。なぜそうなるかについては、もう少し詳しく、こちらの記事に書いた。

 いずれにせよ、請負開発に未来はないので、新たなビジネスを考える必要がある。同じ方が書いた以下の記事には共感できるところが多い。

クラウドコンピューティング、一年後がビジネスチャンス?!


これまでも大手企業が自社の命運を賭して実施してきたビジネスは多くの場合で失敗しています。そしてその失敗の道のりを冷静に観察しているベンチャーや中小企業がその失敗の要因を排除する形で新たなモデルとして成功を遂げていきます。
<中略>
今年の3月辺りから具体的に景気の潮目が変わっています。大手企業では大型プロジェクトの凍結が始まっています。投資に対する見直しが本格化しているのです。こうした大きな流れの中でキーワードは【中小企業】【サービスビジネス】【環境】です。特に中小企業に対するサービスビジネスがクラウドコンピューティングの中心にある概念です。


 それからぜんぜん関係ないけど、Googleマップのストリートビューは衝撃ですな。Google マップ日本版にも「ストリートビュー」機能--道路に立って街中を見渡せる

【Hadoop】 いよいよ具体的に姿をみせてきた このエントリーを含むはてなブックマーク


 今回はHadoopに関する2つのニュースを紹介したい。一つ目はblogeyeの実装の話。これはよくまとまっているよい記事だ。特に目に焼きついた部分が以下。

blogeyeの実装に学ぶ、Amazon EC2/S3でのHadoop活用術


blogeyeではEC2上に構成したHadoopクラスタを通常時は4台、著者属性推定ジョブや実験のためのジョブを投げるときは最大100台近くまで拡大して運用しています。

 ただし100台起動した場合1日で240ドルかかってしまうので、注意が必要です。S3のストレージは非常に安価で提供されており、またEC2からの読み出しは無料で行えるため、EC2で処理する大量データを保管する先としては最適と思われます。
<中略>

ここでMySQLとHDFSを比較すると、下表のようになります。一般的に、頻繁に利用しない大量データはHDFSに、その他のデータはMySQLに置くのが良いと言えるでしょう。


MySQLとHDFSとの比較

MySQLHDFS(分散ファイルシステムを使用した場合、S3を使用した場合共に)
ランダムアクセス速い遅い
レスポンスタイム早い遅い(特にS3を利用する場合は遅い)
データの保管場所マスタS3(スレーブにキャッシュ)
読み書きのスケーラビリティ低い高い


 次にIBMの記事。
IBMが都内にクラウド検証施設、Hadoopのデモも実施


岩野和生氏は、IBMが目指すクラウドを「エンタープライズ・クラウド」と定義する。「エンタープライズ・クラウドはセキュリティやモニタリング、データ保全性などこれまでIBMが蓄積してきた技術で、Webクラウドを補強する。これによって産業界に本当のパラダイムシフトを起こすことができる」


いよいよIBMもクラウドとHadoopに本腰を入れ始めてきた。でも私が在籍したころは、岩野さんのところはもっと基礎的なところをやっていて、前面(ビジネス)に出ることはあまりなかったように思う。時代は変わったのかなあ。こういう話は幕張の旧ATSの方が得意だと思った。思い返すと10年以上前になるが、幕張には並列システム研究室という並列技術を研究する部署がすでにあった。現在の並列技術の盛り上がりぶりについては、所長を除いて誰も予想できなかったのではないだろうか。(そこでは、HadoopのようなMapReduceではなくCORBAを研究していたのだが・・)私はそこに属していた時期もあったが、やれといわれても素直にやったわけではないので、CORBAはあんまり得意ではない。CORBAではなく、まだ勧告にもなっていないXMLをやりたいといったが、まともに相手されなかった記憶がある。当時、XMLはHTMLに毛が生えた程度にしか認識されていなかったのでしょうがないかとは思う。
 しかし、クラウドにせよ、並列技術にせよ、(中島)所長が当時から力説していた、e-DataCenter構想そのものだ。いやあ、所長の慧眼には驚くばかりである。

月曜日, 8月 04, 2008

【JavaScript】 prototype.jsを抜粋して作ったとても小さなAJAXライブラリ このエントリーを含むはてなブックマーク


最近、モッサリ感たっぷりなAJAXライブラリを使うのがとても苦痛になってきたので、必要なところ以外バッサリ切り捨ててみた。このデモ(ここをクリック)
を見てもらうと分かるとは思うが、XMLHttp通信、JSON読込、Object.Extend、bind()、Event.observe()/stopObserving()、Element.show()/hide()、キャッシュ付$()、styleをセットできるBuilder.node()、配列のeach()、any()、$H()、$H(x).toXML()、$H(x).toJSON()などが使える。
これでなんと526行しかない。(ちょっと増えました8/9)
とっても短いAJAXライブラリ extractive.js
 
 デモは左にテキストボックスが現れるので、何か入力してEnterを押すと、右にBOXが描画される。Deleteキーで描画OFF、Insertキーで描画ON、Homeキーで連続表示(each())、ESCキーでAJAX読込、TABキーでJSON読込が実行される。


<extractiveソース>

/**
* extractive.js
*                 
* Functions taken from Prototype library, Log4js
* didn't want to require for just few functions.
* More info at {@link http://prototype.conio.net/}
*            
*/

var Builder = {

   initialize: function() {
   },
   camelize: function(name) {
   var oStringList = name.split('-');
   if (oStringList.length == 1) return oStringList[0];

   var camelizedString = name.indexOf('-') == 0
     ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
    : oStringList[0];

   for (var i = 1, len = oStringList.length; i < len; i++) {
    var s = oStringList[i];
    camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
   }

   return camelizedString;
  },
   node: function(elementname, param, text) {
     var element = document.createElement(elementname);
      if (param.id) element.id = param.id;
      if (param.className) element.className = param.className;
      if (param.style) {
        for (name in param.style) {
           element.style[this.camelize(name)] = param.style[name];
         }
      }
      if (text) {
         var value = document.createTextNode(text);
         element.appendChild(value);
      }
      if (param.src) element.src = param.src;
      return element;
   },
   // http://bmky.net/text/note/javascript-css.html
   //
   getActiveStyle: function ( element, property, pseudo ) {
       if( element.currentStyle ) { //IE or Opera
         if( property.indexOf( "-" ) != -1 ) property = property.camelize( );
         return element.currentStyle[ property ];
       }
       else if( getComputedStyle ) { //Mozilla or Opera
         if( property.indexOf( "-" ) == -1 ) property = property.deCamelize( );
         return getComputedStyle( element, pseudo ).getPropertyValue( property );
       }
        return "";
   }
}

var Prototype = {
 Version: '1.4.0',
 ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',

 emptyFunction: function() {},
 K: function(x) {return x}
}


var Class = {
 create: function() {
  return function() {
   this.initialize.apply(this, arguments);
  }
 }
}

function $(e){return $[e]||($[e]=(document.getElementById(e)||e))}

var Element = {

 hide: function(argument) {
   var element = $(argument);
   element.style.display = 'none';
 },

 show: function(argument) {
   var element = $(argument);
   element.style.display = '';
 }
}

/**
* Object extention (OO) methods if no prototype avaliable.
*
* @private
* @ignore
*/
if(!Object.prototype.extend) {
 Object.extend = function(destination, source) {
  for (property in source) {
  destination[property] = source[property];
  }
  return destination;
 };

 Object.prototype.extend = function(object) {
  return Object.extend.apply(this, [this, object]);
 };
}

if(!Function.prototype.bind) {
Function.prototype.bind = function(object) {
var __method = this;
return function() {
return __method.apply(object, arguments);
};
};
}
var $break  = new Object();
var $continue = new Object();

var Enumerable = {
 
 K: function(x) {return x},
 
 _each: function(iterator) {
  for (var i = 0; i < this.length; i++)
   iterator(this[i]);
 },

 each: function(iterator) {
  var index = 0;
  try {
   this._each(function(value) {
    try {
     iterator(value, index++);
    } catch (e) {
     if (e != $continue) throw e;
    }
   });
  } catch (e) {
   if (e != $break) throw e;
  }
 },
 all: function(iterator) {
  var result = true;
  this.each(function(value, index) {
   result = result && !!(iterator || Prototype.K)(value, index);
   if (!result) throw $break;
  });
  return result;
 },

 any: function(iterator) {
  var result = true;
  this.each(function(value, index) {
   if (result = !!(iterator || Prototype.K)(value, index))
    throw $break;
  });
  return result;
 },

 collect: function(iterator) {
  var results = [];
  this.each(function(value, index) {
   results.push(iterator(value, index));
  });
  return results;
 },

 detect: function (iterator) {
  var result;
  this.each(function(value, index) {
   if (iterator(value, index)) {
    result = value;
    throw $break;
   }
  });
  return result;
 }, 
 findAll: function(iterator) {
  var results = [];
  this.each(function(value, index) {
   if (iterator(value, index))
    results.push(value);
  });
  return results;
 },

 grep: function(pattern, iterator) {
  var results = [];
  this.each(function(value, index) {
   var stringValue = value.toString();
   if (stringValue.match(pattern))
    results.push((iterator || Prototype.K)(value, index));
  })
  return results;
 },

 max: function(iterator) {
  var result;
  this.each(function(value, index) {
   value = (iterator || Prototype.K)(value, index);
   if (value >= (result || value))
    result = value;
  });
  return result;
 },
 min: function(iterator) {
  var result;
  this.each(function(value, index) {
   value = (iterator || Prototype.K)(value, index);
   if (value <= (result || value))
    result = value;
  });
  return result;
 },
 pluck: function(property) {
  var results = [];
  this.each(function(value, index) {
   results.push(value[property]);
  });
  return results;
 }, 
 sortBy: function(iterator) {
  return this.collect(function(value, index) {
   return {value: value, criteria: iterator(value, index)};
  }).sort(function(left, right) {
   var a = left.criteria, b = right.criteria;
   return a < b ? -1 : a > b ? 1 : 0;
  }).pluck('value');
 },
 toArray: function() {
  return this.collect(Prototype.K);
 },
 include: function(object) {
  var found = false;
  this.each(function(value) {
   if (value == object) {
    found = true;
    throw $break;
   }
  });
  return found;
 },
 inject: function(memo, iterator) {
  this.each(function(value, index) {
   memo = iterator(memo, value, index);
  });
  return memo;
 }
}

Object.inspect = function(object) {
  try {
   if (object === undefined) return 'undefined';
   if (object === null) return 'null';
   return object.inspect ? object.inspect() : object.toString();
  } catch (e) {
   if (e instanceof RangeError) return '...';
   throw e;
  }
}

Object.extend(Enumerable, {
 map:   Enumerable.collect,
 find:  Enumerable.detect,
 select: Enumerable.findAll,
 member: Enumerable.include,
 entries: Enumerable.toArray
});

Object.extend(Array.prototype, Enumerable);



var Hash = {
 _each: function(iterator) {
  for (key in this) {
   var value = this[key];
   if (typeof value == 'function') continue;

   var pair = [key, value];
   pair.key = key;
   pair.value = value;
   iterator(pair);
  }
 },

 keys: function() {
  return this.pluck('key');
 },

 values: function() {
  return this.pluck('value');
 },

 merge: function(hash) {
  return $H(hash).inject($H(this), function(mergedHash, pair) {
   mergedHash[pair.key] = pair.value;
   return mergedHash;
  });
 },

 toQueryString: function() {
  return this.map(function(pair) {
   return pair.map(encodeURIComponent).join('=');
  }).join('&');
 },

 inspect: function() {
  return '#<Hash:{' + this.map(function(pair) {
   return pair.map(Object.inspect).join(': ');
  }).join(', ') + '}>';
 },
 toXML: function() {
   return this.map(function(pair) {
    return '<'+pair.key+'>'+ (
     typeof pair.value == 'string' ? pair.value : $H(pair.value).toXML()
    ) +'</'+pair.key+'>';
  }).join('');
 },
 toJSON: function() {
  return '{' + this.map(function(pair) {
    if ((typeof pair.value == 'undefined')||(typeof pair.value == 'unknown')) return pair.key+': ""';
   return pair.key+': '+ (
    (typeof pair.value == 'string')||(typeof pair.value == 'number') ? '"'+pair.value+'"' : $H(pair.value).toJSON()
   );

  }).join(', ') + '}';
 }
}

function $H(object) {
 var hash = Object.extend({}, object || {});
 Object.extend(hash, Enumerable);
 Object.extend(hash, Hash);
 return hash;
}

  /**
   * Attatch an observer function to an elements event browser independent.
   *
   * @param element element to attach event
   * @param name name of event
   * @param observer observer method to be called
   * @private
   */

var Event = {

 KEY_BACKSPACE: 8,
 KEY_TAB:    9,
 KEY_RETURN:  13,
 KEY_ESC:   27,
 KEY_LEFT:   37,
 KEY_UP:    38,
 KEY_RIGHT:  39,
 KEY_DOWN:   40,
 KEY_DELETE:  46,

 element: function(event) {
  return event.target || event.srcElement;
 },

 isLeftClick: function(event) {
  return (((event.which) && (event.which == 1)) ||
      ((event.button) && (event.button == 1)));
 },

 pointerX: function(event) {
  return event.pageX || (event.clientX +
   (document.documentElement.scrollLeft || document.body.scrollLeft));
 },

 pointerY: function(event) {
  return event.pageY || (event.clientY +
   (document.documentElement.scrollTop || document.body.scrollTop));
 },

 stop: function(event) {
  if (event.preventDefault) {
   event.preventDefault();
   event.stopPropagation();
  } else {
   event.returnValue = false;
   event.cancelBubble = true;
  }
 },

 // find the first node with the given tagName, starting from the
 // node the event was triggered on; traverses the DOM upwards
 findElement: function(event, tagName) {
  var element = Event.element(event);
  while (element.parentNode && (!element.tagName ||
    (element.tagName.toUpperCase() != tagName.toUpperCase())))
   element = element.parentNode;
  return element;
 },

 observers: false,

 _observeAndCache: function(element, name, observer, useCapture) {
  if (!this.observers) this.observers = [];
  if (element.addEventListener) {
   this.observers.push([element, name, observer, useCapture]);
   element.addEventListener(name, observer, useCapture);
  } else if (element.attachEvent) {
   this.observers.push([element, name, observer, useCapture]);
   element.attachEvent('on' + name, observer);
  }
 },

 unloadCache: function() {
  if (!Event.observers) return;
  for (var i = 0; i < Event.observers.length; i++) {
   Event.stopObserving.apply(this, Event.observers[i]);
   Event.observers[i][0] = null;
  }
  Event.observers = false;
 },

  observe: function (element, name, observer) {
  if (element.addEventListener) { //DOM event model
   element.addEventListener(name, observer, false);
   } else if (element.attachEvent) { //M$ event model
   element.attachEvent('on' + name, observer);
   }
 },
  stopObserving: function(element, name, observer, useCapture) {
    if (element.removeEventListener) {
     element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
     element.detachEvent('on' + name, observer);
    }
  }
}

var Requester = {

 createXmlHttp : function() {
 
  var x = null;
  if (window.XMLHttpRequest) { // IE7,Firefox, Safari
   x = new XMLHttpRequest();
   return x;
  }
  try {
   x = new ActiveXObject("Msxml2.XMLHTTP"); // IE6
  }
  catch(e) {
   try {
    x = new ActiveXObject("Microsoft.XMLHTTP"); // IE5
   }
   catch(e) {
    x = null;
   }
  }
  return x;
 },
 /**
  * Load a JS-script dynamically.
  * @param {String} src
  */
 
 loadjson : function(src) {

  var documentScripts = document.getElementsByTagName("script");
 
  for (index = 0; index < documentScripts.length; ++index)
  {
   var documentScript = documentScripts[index];
   if (documentScript.src == src) {
    return false;
   }
  }
  
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = src;
  document.getElementsByTagName('head')[0].appendChild(script);
  return true;
 }

}
/* prevent memory leaks in IE */
Event.observe(window, 'unload', Event.unloadCache, false);
var Position = {
 // set to true if needed, warning: firefox performance problems
 // NOT neeeded for page scrolling, only if draggable contained in
 // scrollable elements
 includeScrollOffsets: false,

 // must be called before calling withinIncludingScrolloffset, every time the
 // page is scrolled
 prepare: function() {
  this.deltaX = window.pageXOffset
        || document.documentElement.scrollLeft
        || document.body.scrollLeft
        || 0;
  this.deltaY = window.pageYOffset
        || document.documentElement.scrollTop
        || document.body.scrollTop
        || 0;
 },

 realOffset: function(element) {
  var valueT = 0, valueL = 0;
  do {
   valueT += element.scrollTop || 0;
   valueL += element.scrollLeft || 0;
   element = element.parentNode;
  } while (element);
  return [valueL, valueT];
 },

 cumulativeOffset: function(element) {
  var valueT = 0, valueL = 0;
  do {
   valueT += element.offsetTop || 0;
   valueL += element.offsetLeft || 0;
   element = element.offsetParent;
  } while (element);
  return [valueL, valueT];
 }
}


<デモソース>

<html>
<head>
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<style type="text/css">
.content1 {
  height:10;
  position:absolute;
  margin-bottom:5px;
  background:#FFCC99;
  border:1px solid #FF0000;
  padding:1px 1 20px 1;
  font-size:10px;
  width:100px;
  
}
</style>
<script type="text/javascript" src="extractive.js"></script>
<script type="text/javascript">

var initial = new Array();
initial.push("1");
initial.push("2");
initial.push("3");

var Editable = function() {
  
    this.editbox = $('EditBox');
    Event.observe(this.editbox, "keypress", this.onKeyPress.bind(this));
}

Editable.prototype = {

   onKeyPress: function(event) {
   switch(event.keyCode) {
   
    case 9:        // tab
    
    Requester.loadjson("http://localhost:8081/json.txt");

    try {
     alert("test:"+testjson);
    }catch (e) {}
    return;  

    case 13:        // Enter

    var element1 = Builder.node('div', { className : 'content1',style :{ top : '25',left :'300' }});
    element1.innerHTML = $('EditBox').innerHTML.replace(/<\/?[^>]+>/gi, '')
    $('entity').appendChild(element1);

    alert($('entity').innerHTML);

      return;
    case 27:        // ESC (AjaxRequest)

    this.req = Requester.createXmlHttp();
    var url = "http://localhost:8081/";
    this.req.onreadystatechange = this.callback.bind(this);
    this.req.open("GET", url, true);
    this.req.send("");

    return;

    case 45:        // insert
    Element.show('entity');
    return;
   
    case 46:        // delete
     Element.hide('entity');
    return;

    case 36:        // home
    var contentmain = $('entity');
    initial.each(
      function(value,index) { 
        var content = Builder.node('div', { id : 'id'+index, className : 'content1',style :{ top : 100 + index*30 +'px' ,left :'300' } },value );
        contentmain.appendChild(content);
      }.bind(this)      
    );

   }
   
  },
  callback: function () {
    if(this.req.readyState == 4) {  // 読込完了
      if(this.req.status == 200) { // HTTP OK
        alert(this.req.responseText);
      }
      else {
        alert(this.req.status);
      }
    }
  }

  
}
</script>
<title></title>
</head>
<body>

<DIV id="EditBox" style="float:left;width:200;height:500;background:white;border:silver 1px solid;padding:3px" contenteditable>
<BR><BR>
</DIV>

<div id="right" style="float:right;">
<div id="entity"></div>
</div>

<script type="text/javascript">
new Editable();
</script>

</body>
</html>

日曜日, 8月 03, 2008

【お知らせ】 バーチャルテクノロジーは第二ステージへ このエントリーを含むはてなブックマーク


 本日、トップページを更新した。弊社の取り組みと方針

 第一ステージの命題は果たせたし、今の会社の実態に合わなくなってきたので、いつかは更新しなきゃなあとは思っていた。でも、なかなかできなかったのは、SaaSとクラウドのどちらに比重を置くべきかを悩んでいたためだ。これは勝手な思い込みではあるが、SaaSはお客様業務と直結するので、カストマイズ対応には限界がある。一方、クラウドは検索などの機能に限定することで対応可能である。
 では、クラウドで実際に何をするかというと、まずは分散KVSに対応したReflexを使った検索機能で、次に、受注データや在庫データなどを管理できるサービスを提供する。最終的にはECサイトを構築したいと考えている。

 ここまでが第二ステージ。

 ここから第三ステージ(妄想開始)
 ITのパラダイムがシフトして、クラウドに代わるもの(きっとP2P)が登場するだろうと考えている。第三ステージはそこで動くフレームワークとなるだろう。イメージはここに書いているようなものだ。Google技術のオープンソース化がきっかけで、名前はまだないが、SETI@Homeのような並列処理が可能な、自前クラウドが出現してくると思う。そして、「Fuck!あちら側」運動が起きると予想する。これが現実味を帯びるためには、フォルトトレーラントとセキュリティが鍵となるだろう。これは5年後ぐらいの話だ。(妄想終わり)

<第三ステージのReflex>

Reflexは、「P2Pで安全に商取引ができるインフラの提供」を目的としたミドルウェアであるが、なぜP2Pにこだわるかをここでちょっと説明したい。

一極集中から新しいパラダイムが生まれて分散へ流れ、それから、また集中に動く。
これをITにおける輪廻という。 (by IBM 中島DE)

Yahoo やGoogleといった巨大なポータルサイトもあるが、インターネットは、原理的にはIPによる個々のコンピュータによるネットワークである。すなわち、基本的に分散型であり、インターネットの特長を最大限に生かすには、P2Pのような分散型が最適であると考えられる。

今、WEB2.0という新しいパラダイムが生まれて、CGMという個人の情報が重要だと認識された。
CGMを最も効率よく流通させるのは、インターネットの特長を生かしたP2Pであることは、疑いようがない。

また、今後膨大なCGMが発信するようになると、1極集中型が破綻すると私は予想している。ムーアの法則がサチッてきている以上、横に広がる以外ないのだ。


 ちなみに、第一ステージは何だったかというと、オープンソースによる基幹システムの構築とWebサービスの活用である。Reflexの基本的な部品を作ったばかりだったので、それを実際のシステムに入れてみたかった。今ちゃんと動いているので、命題は果たせたことになる。ということで、第一ステージクリアー。(SaaSはいろいろ問題があってできなかった。前記事参照)

<2007年抱負>


今年は、ある会社のネットビジネスを手がける予定だ。Ajax、などWeb2.0バリバリで、また、SaaSのモデルもやりたい。たぶん、その会社のCTOとして参加することになると思うが、正式に決まったらお知らせしたい。

あれだけ去年熱く語っていたReflexはなんだったんだ?と思わないでほしい。このビジネスでも、Relfe CoreやReflex iTextなどは、どんどん活用していくつもりだ。P2P機能は次のフェーズになるとは思うけど、まあ、リアルビジネスが優先されちゃうのは常ということで・・・。(いかんいかん)

金曜日, 8月 01, 2008

【雑記】 妄想族の憂鬱 このエントリーを含むはてなブックマーク


 妄想をしなければ生きていけない人のことを妄想族という。いつも夢みたいなことばかり言う人たちである。バーチャルテクノロジー≒妄想技術である当社の社長も妄想族の一人である。先日、IBMの先輩と話をする機会があった。「IBMにいる頃は、あなたは夢見たいなことばかりいってたけど、独立して変わりましたね。」といってくれたが、実は何も変わっていないことで恐れられているのを知らないだろう?IT業界は社長だらけである。福岡の室見川の橋の下にも社長がいっぱいいる。妄想族と引きこもりは紙一重なので、頑張って社会と繋がっていなければならない。バカにされることが多いかもしれないが、妄想はやめてはならない。妄想が現実になると、ビジョナリーとして一転して尊敬されるようになることもある、という妄想を同じ妄想族で(ある社長)と飲み会で話した。いやあ、妄想族は生きていくだけでも大変なのに社員を養っていくなんて大したものだ。独立すれば自由は得られるが孤独感・不安感に苛まれることになる。それに打ち勝つには妄想があればよい。独立の条件=妄想族といいたいところだが、実際は妄想を抑えきれなくて独立したという方が正しい。
 さて、話が変わって、最近の若者について。一つ目はHadoopを研究している大田さんとその関係者。
YLUG 第88回カーネル読書会 Googleの基盤クローンHadoopについて(このビデオは難しいけど勉強になるのでおススメだ)
 彼のグループ、はてな、それから学生ベンチャーの会社が合同で並列技術(Hadoop)を研究して、一部の機能をはてなの検索に使っているらしいのだが、これはとてもいい時代になったなと思った。はてなは日本のGoogleになろうとしているのかもしれない。梅田さんや近藤さん、Naoyaさんたちは、これまでにない価値観で日本のベンチャーの先駆けとなってくれている。それに優秀な学生がついていっているのは本当に明るい話である。優秀な学生は官僚や大企業に入ってスポイルされてはいけない。やはり、新しい価値を生み出せるところにどんどん挑戦していくべきだろう。それにはやはり夢が必要なんだが、はてなにはそれがあるということかもしれない。ちなみに私は妄想ということで夢を語ることを続けていきたい。(誰だ!笑っているのは・・)
 一方の優秀な学生の記事を読むと、[ルポ]弾氏への応答
 ・・・ちょっとガッカリ。何がガッカリかというと、なんというか、弾さんはたしかに有名人ではあるが、ちょっと失礼かもしれないが、夢の対象や人生の目標ではないだろうし、それほど突っかからなくてもいいのではないかと思ったからだ。既存の社会の価値観が壊れていくなかで、就職はたしかに難しい話ではあるけれども、もう少し余裕があってもいいんじゃないかなあ。世の中、妄想だけで生きてる人がいるというのに・・・。学校で教えるべきことは妄想力を鍛えることなのかもしれない。

 
© 2006-2015 Virtual Technology
当サイトではGoogle Analyticsを使ってウェブサイトのトラフィック情報を収集しています。詳しくは、プライバシーポリシーを参照してください。