木曜日, 7月 31, 2008

【EC開発体験記-SaaS2-】 リスクをどう考えるか このエントリーを含むはてなブックマーク


 今回は、前記事で説明できなかった、SaaS利用における安全性・信頼性の話をしたい。SaaSは、以下のように、提供者、利用者の両方に大きなメリットがある、すばらしいモデルである。(太字が重要なキーワード)
 サーバやソフトウェアへの初期投資は必要なく、トランザクションが増大してもシステムリソースの心配はない。また、必要なサービスだけに絞って開発することで初期投資を抑えることができる。

 1)個々のユーザが本当に必要な機能のみオンデマンドカストマイズして利用できる
 2)クラウドコンピューティングを利用することで、スケーラビリティのあるコンピューティングリソースを使うことができる
 3)利用した分だけを支払う従量課金により投資リスクを軽減できる


 こんな魅力的な概念ではある一方で、カストマイズがどれだけ可能かという話や安全性・信頼性の問題があるのは周知の事実である。カストマイズの話は前記事でも触れたのでここでは省略する。
 安全性・信頼性の問題は、主にはサービスレベルの保証と情報漏えいリスクの問題だ。
 サービスレベルの保証については、実際にAmazon S3の障害事例が起きたときのことを考えてみるとよい。Amazon S3、何も日曜に落ちなくてもいいのに!
 とにかく、サーバがダウンしたときに自分たちで復旧できないことが一番痛い。アマゾンEC2などのクラウドコンピューティング利用を考えるのであれば、サーバのサービスレベルはアマゾンに依存することになるが、それはつまり、アマゾンが止まればサービス提供者でさえ何もできず「ただ復旧を祈るだけ」となることを意味する。
 私たちのプロジェクトにおけるサーバ利用はどうだったかというと、1.自社サーバルーム内、2.都内S社ハウジング、3.都内P社(セミ)ハウジング、4.都内P社ホスティング、5.関西S社ホスティングの5箇所で、家具や家電、DVDサイトをそれぞれの場所で運用したのだが、やはり運用上一番安心なのは自社サーバルーム内であった。(運用上安心というのは、サーバがダウンした際にすぐにリカバリーできるかということであり、セキュリティが確保されているという意味ではない。)安心な順でいえば、1>2>3>4>5といった感じだ。
 システムはトラブルがつきものであり、すばやくリカバリーできるかどうかは運用上とても重要なことである。実はこんな苦い経験もあった。
 ドロップシッピングサイトは5.関西S社ホスティングで運用していたのだが、トラブルでシステムダウンしたことがあった。再起動しても復旧しないし、最悪なことにログインすらできなくなった。その日受けた千件の受注を処理できないどころか、データを取り出すことさえできないのである。ホスティング会社に連絡してなんとか対応してくれるように頼んだものの「不可能です」の回答。私はとても後悔した。そもそも、月額数千円のホスティングサーバをクリティカルな業務に使用してはいけない。しかも千件/日という多くの受注を処理するなんて無謀である。その後、単なるパスワード間違いでログインできなかったことがわかり復旧できたのだが、本当に顔面蒼白事件であった。同様のことが4.都内P社ホスティングで起きたのだが、このときはサーバ管理しているビルまで上がりこんで責任者に懇願して事なきをえた。こんな事件が頻発したこともあり、ホスティングはだめだなと思い新規に契約したのが2.都内S社ハウジングであった。ここは自由に入管可能であり、都内なので直ぐいくことができる。しかし、サーバやルータは自前で用意しなければならず、家賃も高いのが問題だ。今のところ、コスト面、運用面で優れていると思っているのが、3.都内P社(セミ)ハウジングである。サーバはP社製のものを購入する必要はあるものの、サーバ費用も家賃も安い。入管も可能なので安心である。一方、クラウドコンピューティングはどうかというと、安心度は5.以下というところが実情だろう。最低でも冗長構成の仕組みを導入してトラブルがあっても止まらないようにするか、あるいは、別のサーバを使ってすぐに復旧できる仕組み作りは必須だと思う。

 次にデータの安全性確保について。

 Gmailを会社の標準にしたいが情報漏えいした場合に保証がないのでできないという話もよく聞く。ある大手の会社では、社内基幹サーバを大手ベンダーのアウトソースしており、情報漏えいがあった際には、アウトソーサーが賠償責任を負うことになっている。こういった契約をGoogleと結ぶことはまず無理である。なので、大手の会社は自分たちのリスクとして負わないかぎり、Gmailを利用することはできない。実際のところアウトソーサーよりGmailの方が安全なのかもしれないが自社へのリスク回避ということで考えると賠償責任を求めることができないと採用されないだろう。でもまあ、これは現在がこのような状況であるというだけで、将来的は賠償責任がなくても、もっと利用されていくだろうと楽観的に考えている。それは確信はまだ持てないが、SaaSを利用した企業の競争力は無視できないほど高まると思うからである。
 一方のSaaSの提供者は、賠償責任までとはいわないまでも、以下のような認定をとるなど、安全対策についてはかなり努力しないといけないだろう。

ASP・SaaS安全・信頼性に係る情報開示認定制度

 最後に、実際に事故が起きたときのために賠償保険に入るのはよいことだと思うのでちょっと紹介したい。

法人/企業向け賠償責任保険

 当社のような小さな会社は特にいえることだが、大手の責任転嫁を簡単に受け入れてはいけない。個人情報保護は多くのところが無限責任を要求してくるが、契約したいからといって簡単にサインしてしまってはだめだ。事故は絶対に起こらないとは限らないので、お客様にきちんと説明して賠償責任保険に入るようにしよう。小さな会社に無限責任を押し付けたって実際に責任を取れるわけではないので、保険に入ることが結果的にお客様への利益にもなるのだ。「いやいや、うちの会社は無限責任を要求することはまずないから、入る必要はないよ」とお客様がおっしゃるのであれば、契約書の無限責任の条項を削ってもらうようにしよう。

火曜日, 7月 29, 2008

【EC開発体験記-SaaS-】 カストマイズは本当に可能なのか このエントリーを含むはてなブックマーク


 今回の話はSaaSである。私は当初、自社基幹システムを含めたサービスの利用、つまり、システムをすべて外部業者に委託できないかということを真剣に考えていた。そもそも、私の本来の目的はECを開発するのではなく、ビジネスを黒字化することであったので、極端な話、EC事業をサービス化した外部の会社があって、サービスを利用できるのであれば開発しなくてもよかったのだ。実際に自社サイト以外にも、楽天やYahooといったモールを利用しており、これで全て業務ができるのであれば結構な話である。しかし、実際には下図のようにEC機能としては十分ではあるものの、出荷、販売管理といった機能がないため、自社の基幹システムに受注を取り込む必要があった。





 SaaSは定義が曖昧で分かりにくいという話はよく聞くので、次の記事を参照いただきたい。ASPとの違いという観点でよくまとまっている。

代表的なSaaS企業といわれているSalesForce.comのモデルを説明した記事(SaaSの衝撃(2) セールスフォースの凄み

セールスフォースの提供するSaaSは従来のASPとは異なる。なかでも特筆すべきは、ASPが抱えていたカスタマイズの難しさや、外部のアプリケーションとのデータ連携、 システムの信頼性への疑問といった問題点を解決したことだ。


 逆の言い方をすると、自社システムを用意しなければならないのは、カスタマイズの難しさや、外部のアプリケーションとのデータ連携があるからである。外部の業者、例えば、楽天やヤマトのシステムに集約できれば話は簡単であるが、より多くの機会やきめ細かなサービスをやるためには、サイトも配送業者も一つだけでは不十分である。実際のプロジェクトでは、様々なフロントエンドのECシステムとの連携、および、大・中・小の3つの配送業者のシステムとの連携を行う必要があったので、自社基幹システムを開発することになった。
 もっといえば、商品によってもサービスが異なってくる。今回のECシステムでは、家電の受発注品を皮切りに、家具、DVD、そしてドロップシッピングと、様々な商品に対応すべく拡張していったが、それぞれにおいて受注の方法、管理の方法が異なるため、実際にはまるで別のシステムをゼロから構築するかのようにカストマイズせざるを得なかったのである。私はECについては素人であり、商品が異なっても仕組みはどれも似たようなものだろうと甘く考えていた。まさかここまで違いがあるとは思いもしなかった。一例を挙げよう。

 <家電受発注品>
  1) 注文を受けてから発注するので納期は曖昧である(3日~7日)
  2) 基本的に在庫管理しない。(受注を受けられれば配送できる)
  3) 小物であればお客様もお届け日の指定にこだわらない
  4) 粗利が低いため、配送料は配送地域によって差をつけたり、高額商品であれば無料にする
  5) ドロップシッピング品においては返品が不可

 <家具>
  1) 家具は在庫管理が必須である
  2) 大物が多いため納期はお客様指定の日を厳守する
  3) 配送管理におけるLT計算が必要になる
  4) 商品の大きさにより配送料をきめる。また、設置、組み立てサービスを別途用意する

 <DVD>
  1) 予約販売が必須であり在庫管理が必要
  2) 発売日の発送を厳守する
  3) 受注を受けてから配送までの時間が長い(半年などもある)ため、すべての商品を発送しないうちに代金を回収することがある(一品発送をもって決済)
  4) 発送枚数が1枚であれば配送料を安くできる(メール便)


 それから、大切なことで細かい話ではあるが、ぜひ気をつけておきたいことに、受注の括り方と金額の計算方法の統一化がある。受注金額の計算では、一品一葉単位であれば商品ごとに消費税が加算された合計となるが、受注単位であれば受注金額の和で消費税計算をすることになる。また、小数点以下は切り上げ(常にお客様が得になるように計算する)とするなど、計算方法がシステム全体で統一されていないと不具合となる。
 先ほど、もし、モールに受注・出荷・販売といった管理機能があればとはいったが、これだけの違いを吸収するだけでも大変である。ASPであれば、パッケージソフトのように出来ることだけを選択すればよいが、カストマイズを前提にしたSaaSでは、基本は全ての要求に応えなければならないだろう。そうなると果たしてどこまで対応できるか疑問である。
 たしかに、費用と時間をかければ不可能ではない話ではあるが、SaaSは多くの顧客に対応することでスケールメリットを追求するモデルなので、それほど多くをかけられないというのが現状だろう。また、通販というビジネスモデルを考えると妥協できないのは運用コストである。結局、システム運用・保守に多くのコストをかけてしまうのであれば、SaaSにする意味がなくなってしまう。
 これについては、私たちのプロジェクトに当てはめてみると反省すべき点が多い。そもそも当初の目的である受注センターの効率化のために、異なる商品の受注業務を一本化して、システムと人の統合を進めなければならなかった。しかし、実際は家電・家具・DVD用のそれぞれの受注システムとスタッフが縦に並ぶ構造になってしまった。商品の特徴を知るバイヤーを中心に販売方法を決めていたこともあったので3チーム編成は悪くはなかったとは思うが、受注スタッフの縦割りは明らかに非効率であった。ここでも書いたが、コストを最小化できてはじめて利益を高めることができ、あるいは、お客様にサービスとして還元できるようになる。もう少し踏み込んだシステム統合、および組織改革は必要だったと思う次第である。

 なお、SaaSの利用については、安全性・信頼性といった問題も考えなければならない。これはまた別の機会に触れたいと思う。

ASP・SaaS安全・信頼性に係る情報開示認定制度

月曜日, 7月 28, 2008

【IBM sMash】 Groovy コンファレンス 2008 このエントリーを含むはてなブックマーク


この話を中心に、sMashとReflexのコラボについて話す予定なのでぜひ見にきてください。デモもやるけど、System-iでできるといいなあ・・・。2008年8月22日 sMash(ProjectZero)と「Reflex」で構築するお手軽営業支援システム @IBM SWCOC セミナールーム 渋谷区道玄坂一丁目12-1(渋谷マークシティウェスト18階)

金曜日, 7月 25, 2008

【Reflex】 ResourceOperator公開を機にあらためて紹介 このエントリーを含むはてなブックマーク



 トランザクション処理に対応したResource Operator、Resource Locator、また、Source Generatorが完成したので近日中に公開したいと思う。Reflexフレームワークについては何度か説明してきたが、これを機にあらためて概要を説明したい。上図にあるように、Reflexは、3つのサーバコンポーネントとクライアントのコンポーネントから構成される。
 まず、Reflex Coreに含まれる、Resource Mapperは、シリアライザー・デシリアライザーで、XMLやJSONからJavaBeanに変換するAPIをもつ。
 Reflex Operatorは、Entityに対してCRUD操作ができるようにするためのコンポーネントで、ローカル/リモートを意識することなく呼び出せることが特長である。リモートのEntity呼び出しは、JNDIにURIが登録されているかどうかで判別される。現在のところ、トランザクション処理はローカル呼び出しのときのみ有効となる。(そのうちリモートでも対応する)
 Resource Locatorは、EntityのURIを解決するためのコンポーネントである。これ自身がEntityとして振舞い、JNDIにURIを登録・削除する機能をもつサービスとなる。Source Generatorは、項目の文字列からEntityのソース、およびSQLを自動生成するツールである。CoCを前提にしたフレームワークでは、マッピングなどで名前が厳密に定義されないといけないため、自動生成ツールは必須だと考え作成した。
 Reflex Viewは、Entityの内容を元にHTMLやPDFを生成するコンポーネントである。HTML生成はブラウザのJavaScript、PDF生成はサーバのライブラリ(Reflex iText)で動作する。詳細はこちらを参照願いたい。ワンソースマルチビューを実現する



 上図は、Reflexの各コンポーネントを配置したものである。一番大きな水色枠がサーバ一つを表している。サーバは、Tomcat、Jettyを想定しているが、Resource LocatorはJettyでないと動かない。紫の枠がReflexのフレームワーク、緑の枠が自動生成されたコンポーネントなので、手で書く必要のあるものは、実質、黄色のBlogicだけである。
 今回公開予定のResourceOperatorは、プロジェクト開発の経験、反省点を踏まえて改良したものである。トランザクションスクリプトパターンを採用しており、成功時にだけコミット、失敗してExceptionがスローされたときにロールバックされるようになっている。このように、トランザクションを開発者に管理を意識させないように工夫している。トランザクション管理はDIを使ったものが主流だが、Reflexではコンパクトにしたかったので使用していない。今回は、Resource Operator、Resource Locator、Source Generatorの3つを公開する。Reflex iTextはサービスとして公開予定なのでオープンソースにはしないがReflex View(HTML)はそのうち公開する。また、Reflex MapReduceもサービスとして公開予定である。
 


 ここでちょっと上図を見てほしい。これは、2004年にXMLコンソーシアムで発表したときのプレゼン資料の抜粋であるが、Reflexのコンポーネント図とよく似ていることがわかるだろう。当時はSOAPが主流だったので通信のコンポーネントにAXISを使い、バインディングツールにはCastorを使っていた。Castorを使った開発はすばらしく生産性が高かったので、その後のReflexの開発の動機となった。
 この図は、暗号化用のXSS4Jなどが含まれているものの、Reflexと比べると複雑である。また、AXISでは、クライアントとサーバは明確に区別され1対1で接続されるが、Reflexでは、サーバとクライアントが対称で区別せず、1:多で複数のコンポーネントを接続できる。
 


 最後にぜんぜん関係ない話をひとつしたい。
 2005年のプレゼン資料をあさっていたら、AJAXとRCPの比較なんてものがでてきた。当然、間違いだらけである。これを今訂正するとしたら、「ビジネスロジックを分担するまでには至らない」だろうか。Reflex Viewにおいても、ブラウザ上のJavaScriptビジネスロジックを認めている。
 逆に、「高い技術力、センスが要求されるので、一部の開発者でなければ無理」は、概ね当たっているのではないだろうか。開発者のスキルあるなしで、特にパフォーマンスの面で雲泥の差となる場合が多い。AJAXが注目されつつある時期だったのでRCPをひいきめに書いた記憶がある・・それでもAJAXは大流行したのであるが・・・・。
2005年プレゼン資料

土曜日, 7月 19, 2008

【EC開発体験記-ビジネスモデル-】 メーカ直送は究極の通販モデル このエントリーを含むはてなブックマーク




 通販業者は店舗をもたないため、その分経費がかからず、製品開発やマーケティング、お客様サービスに費用をまわせるというメリットがある。・・・少なくとも消費者からはこのように思われている。しかし、実際にはコールセンターを含めた受注コスト、商品開発コスト、物流コスト、システム運用費用など多くのコストがかかっている。うまくいっていない通販ほど、かかるコストが大きく、売れば売るほど赤字という悪循環に陥っているのが実態である。私が担当したときの会社はまさにこのような状態であった。通販事業を行うにあたって最も注意を払うべきことはコストである。コストを最小化できてはじめて利益を高めることができ、あるいは、お客様にサービスとして還元できるようになる。こう信じたからこそ、まず最初に取り組んだのはコスト削減、とりわけ、物流改革による運賃、管理費の削減であった。具体的には次の2点を中心に取り組んだ。
 
 1)外部業者に委託していたロジスティクス業務を自社にて行う
 2)メーカから直接お客様に届ける仕組みを構築する

 外部ロジスティクスをやめることは大きな決断であった。そこにかけていた経費を考えるとどうしても避けては通れないことは明らかだったが、増員を見込めない自社で本当にできるかどうかわからず、大きな不安があった。不必要な業務があることが分かっていたので廃止していくものの、それだけでは不十分である。抜本的に業務を削減するには、システムによる自動化・省力化を図る必要があった。また、小・中の商品については、必ずしも大・特大と同じ配送業者を利用する必要はなかったので、小・中を得意とする配送業者と新たに契約することで配送コストを下げた。さらには、メーカに集荷して直接お客様にお届けする、メーカ直送という仕組みを導入することで管理コストを下げた。これらにより、物流コストを1/3に抑えることに成功したのである。
 システムによる自動化・省力化については、「一品一葉」という概念の導入と徹底した配送管理によって実現している。大・中・小と様々な商品を一度に購入されたお客様でも、システム上は「一品一葉」で管理されているため、各配送業者に注文が分かれても問題なく処理できるようになっている。また、配送管理については、まず出荷指示を配送業者のシステムに送信することで集荷依頼を行い、次に、一品ごとの状態(出荷済、配達完了等)を配送業者のシステムから受信することでステータスを更新する。返品の際も、配送業者に返品の指示をデータ送信するだけでお客様のところに集荷してメーカに返品してくれる。
 さらには、お客様のお届け日を指定でき、Webサイトからでも電話注文でも指定可能となっている。この仕組みはお届け日から逆算して、メーカへの出荷指示日を計算できるテーブルを作成しているからできているのだが、配送業者も興味を示すほどの仕組みであり、おそらく他に実現しているサイトはないと思われる。このように、配送システムとの自動連携により、管理の自動化ができているのである。
 実際に省力化が実現できたとき、そのコスト削減額は想定よりもはるかに大きいことがわかり、あらためてシステム化による効果の大きさを知った。
 今回、暮らしのデザインはニッセンの新しい一員となったが、ニッセンの社訓の最初にはこのように書かれてある。

1.常に物流を改善していく


木曜日, 7月 17, 2008

【iPhone】 2つの見方 このエントリーを含むはてなブックマーク


以下の順番で読むと面白い。
iPhoneという奇跡
iPhone 3G のダメダメなところまとめ
携帯電話として見るとつまらんものらしい。PCとして見ても同様である。
では、iPhoneをなんで欲しいと思うか。それが問題だ。

【IBM sMash】 ReflexとsMashによるソリューションの提案 このエントリーを含むはてなブックマーク



 これは先日あるお客様に相談されて考えたものだが、もったいないので当社のソリューションにすることにした。そのお客様は中型汎用機(IBM System-i)のユーザであり、数十箇所の拠点ごとにシステムを置いている。本提案は各拠点のサーバをマッシュアップしてやろうというものだ。具体的な内容は以下のとおりである。また、デモをこちらで配布している。=>PMS Demo

<現状>
 1.営業支援システム(SFA)が各拠点にあるIBM System-iのうえで動いている
 2.営業はこのシステムを使って日報入力や在庫検索をしているが、拠点に戻らないと使えないので不便を感じている。

<相談内容>
 1.営業支援システムをインターネットに接続して携帯端末から使えるようにしたい

<課題>
 1.各拠点に分散したサーバへのアクセス手段がない
 2.各拠点にFirewallなどのN/W設備やアプリケーションを導入しなければならない
 3.そもそも各拠点の基幹システムにインターネットから直接アクセスするのはセキュリティ上問題がある
 4.Webアプリケーションを各拠点にデプロイしなければならない
 5.ネットワークを管理・監視しなければならない

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

<効果>
 1.セキュリティの確保
 2.Webアプリケーションの一元管理
 3.携帯端末に限らず、あらゆるWebアプリケーションに応用可能

 このソリューションは、Webサービス化とマッシュアップ技術により、System-iや汎用機といったプロプライエタリシステムとの連携を可能とするものだ。以前にも述べたが、IBMの既存のお客様はこのケースのように、Web化によって付加価値を見い出せるところが多いはずである。参考
 sMashのSystem-i対応は、IBM開発元ともう少し詰めなければならない部分があるそうだが、そこはsMash開発者の一人である樽澤さんにサポートいただき課題を解決していきたいと思う。

もしこの提案にご興味あれば遠慮なく以下まで連絡ください。

takezaki アット virtual-tech.net

金曜日, 7月 11, 2008

【雑記】 iPhoneの魅力  このエントリーを含むはてなブックマーク



私の結論
  
音楽とソフトの新しい流通網を創る可能性があるから

 ソフトバンクは日本のパソコンの「パッケージソフト」流通網を創り出したが、今度はアップルの手を借りて新しい「ネットワークソフト」流通網を創ろうとしている。これは、どちらかというと提供者側にとって魅力的に感じるところかな。新しいソフトを開発してiPodで儲けてみようと思う者にとっては非常に魅力的なものに見えるだろう。当然、私も魅力的に見える。

でも、消費者が欲しがる動機って何なんだろう?

水曜日, 7月 09, 2008

【JavaScript】 Validate時にfocusを固定する方法 このエントリーを含むはてなブックマーク


 今回は、Reflex Viewを担当している genokitaさんからの嵌りの報告。Reflex Viewで、キー入力時にvalidatorが実行されるところで嵌ったようだ。 validatorは、例えば、値をチェックして「誤」と認められた場合に、入力中のテキストボックスの背景を赤色にしたりカーソルを動かなくしたりする機能である。Reflex Viewの概要およびValidatorについてはこちらを参照していただきたい。
 まず、validatorの基本動作について、A,Bの2つのテキストボックスを例に以下に説明する。2つのテキストボックスにはonblurイベントが登録されており、validatorはonblurイベント駆動になっている。onblurイベントは、カーソルがテキストボックスから離れたタイミングで発行されるイベントだ。


A:テキストボックス
B:テキストボックス

【動作】
Aの値を誤に変更し、Bをクリックする

【前提条件】
(1)両テキストボックスにはonblurがかかっている
(2)blurイベントが発生するごとにvalidationがかかる
(3)validationが通らなかったら、フォーカスを誤セルに戻し、表示される値は誤のまま(=entityの値を入れなおさない)
(4)validationが通ったら再全計算(Blogic()),再描画(Refresh())がかかる

【ロジック】
if (A.blur) {
res = validate(A);
}

if (res == IS_INVALID)
alert("A is invalid!");
A.focus(); --(i)
} else if(res == IS_VALID) {
Blogic();
Refresh();
B.focus();
}

(i)に至るまでには
A.blur;
B.focus;
B.blur;
A.focus;
の順にイベントが発生するので、B.blurの時点でBのvalidationがかかる。



 詳細は以下にまとめるが、今回の問題は、validator実行後にカーソルが移動してしまうことが原因で起きていた。強引にフォーカスを元に戻そうとすると移動先のonblurイベントによるvalidatorが走ってしまうが、元のテキストボックスが「誤」であるから、移動先のテキストボックスが「誤」である場合に無限ループを引き起こしてしまうのである。ちなみに、読込時の初期データに誤りがあっても同様の現象に陥るため、前提条件をクリアーしたデータでないとうまく動作しない。このことは入力点におけるデータチェックの必要性ということで、こちらに書いたとおりである。


【初期データに誤りがある場合】

 A初期:正
 B初期:誤

* Aの値を誤に変更し、Bをクリックしてしまうと、AとBが交互にエラーを吐くため無限ループに嵌る(←ここで嵌った)
* 初期データは正しいという前提で黙認(本当なら初期データが正しいかどうかをチェックしてあげなきゃいけない)

【初期データが正しい場合】
 
 * Aに誤を入れた後にBをクリックすると、A.blurで最初のvalidationが実行され、B.blurの時にも再びvalidationがかかる
 * blurの度にvalidation再計算再描画が行われるため、B.blurの時にAの値がentityの値で置き換えられてしまう(←ここで嵌った)



 解決策としては、以下のように、本来格納すべき場所(Entity)以外の一時的な値を保持する領域を用意すればよい。Reflex Viewでは、これをpreview層と定義している。preview層に入っているデータはまだValidatorを通っていないためエラーの可能性がある。Validatorでエラーでないことが確認できればEntityの値を書き換えることができる。



【解決策】

* 誤Aの値をどこかに格納しておき、再描画の際にentity.Aを誤Aで置換してやればいい
* Validator(View)クラスを作成し、誤セルのID・値・エラーコードを静的保持しておけばいい




火曜日, 7月 08, 2008

【JavaScript】 DOMをXMLに変換 このエントリーを含むはてなブックマーク


 今回のXML変換は連想配列ではなくDOMから変換するものだ。これは、Googleのコードを抜粋したもので、2006年頃に使っていたものだが、DOMはあまり好きではないので捨て置いていた。しかし、この記事が好評なので紹介することにした。さらに連想配列に変換したければjson.jsを使うといいだろう。
dom2xml.html DEMO(ココをクリック)

<dom2xml.html: サンプル>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DOM2XML</title>
<script type="text/javascript" src="scripts/xmltext.js"></script>
<script type="text/javascript" src="scripts/xmlgetx.js"></script>
<script type="text/javascript">

function test() {

 var collection = document.createElement('collection'); // ルートエレメントを作成
 collection.setAttribute('title','Java Script XMLDOM test'); // 属性を作成

 var membertype = document.createElement('member-type');  // 子要素を作成
 membertype.appendChild(document.createTextNode('jpeg')); // 子要素のtextnodeを作成
 collection.appendChild(membertype);            // 親要素に子要素を追加

 var xmltext = xmlText(collection);   // xmlのタグを含む形でテキスト化
 alert(xmltext);

 var xmlvalue = xmlValue(collection);  // xmlのタグを除く形でテキスト化
 alert(xmlvalue);
  
 var title = xmlGetAttribute(collection, 'title'); // xmlの属性を取得する
 alert(title);

 var membertype = xmlGetText(collection,'member-type'); // xmlのtextnodeを取得
 alert(membertype);
  
}

</script>
</head>
<body onload="test();"></body>
</html>


<xmlgetx.js: 属性やテキストノードを取得する(クリックでソース参照)>

function xmlGetText(node,tagname) {
 var result = node.getElementsByTagName(tagname).item(0).childNodes.item(0).data;
 return result;
}

function xmlGetAttribute(node, name) {
 // TODO(mesch): This should not be necessary if the DOM is working
 // correctly. The DOM is responsible for resolving entities, not the
 // application.
 var value = node.getAttribute(name);
 if (value) {
  return xmlResolveEntities(value);
 } else {
  return value;
 }
};

// Split a string s at all occurrences of character c. This is like
// the split() method of the string object, but IE omits empty
// strings, which violates the invariant (s.split(x).join(x) == s).
function stringSplit(s, c) {
 var a = s.indexOf(c);
 if (a == -1) {
  return [ s ];
 }
 
 var parts = [];
 parts.push(s.substr(0,a));
 while (a != -1) {
  var a1 = s.indexOf(c, a + 1);
  if (a1 != -1) {
   parts.push(s.substr(a + 1, a1 - a - 1));
  } else {
   parts.push(s.substr(a + 1));
  }
  a = a1;
 }

 return parts;
}

// Copyright 2005 Google Inc.
// All Rights Reserved
//
// An XML parse and a minimal DOM implementation that just supportes
// the subset of the W3C DOM that is used in the XSLT implementation.
//
// References:
//
// [DOM] W3C DOM Level 3 Core Specification
//    <http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/>.
//
//
// Author: Steffen Meschkat <mesch@google.com>

// NOTE: The split() method in IE omits empty result strings. This is
// utterly annoying. So we don't use it here.

// Resolve entities in XML text fragments. According to the DOM
// specification, the DOM is supposed to resolve entity references at
// the API level. I.e. no entity references are passed through the
// API. See "Entities and the DOM core", p.12, DOM 2 Core
// Spec. However, different browsers actually pass very different
// values at the API.
//
function xmlResolveEntities(s) {

 var parts = stringSplit(s, '&');

 var ret = parts[0];
 for (var i = 1; i < parts.length; ++i) {
  var rp = stringSplit(parts[i], ';');
  if (rp.length == 1) {
   // no entity reference: just a & but no ;
   ret += parts[i];
   continue;
  }
  
  var ch;
  switch (rp[0]) {
   case 'lt':
    ch = '<';
    break;
   case 'gt':
    ch = '>';
    break;
   case 'amp':
    ch = '&';
    break;
   case 'quot':
    ch = '"';
    break;
   case 'apos':
    ch = '\'';
    break;
   case 'nbsp':
    ch = String.fromCharCode(160);
    break;
   default:
    // Cool trick: let the DOM do the entity decoding. We assign
    // the entity text through non-W3C DOM properties and read it
    // through the W3C DOM. W3C DOM access is specified to resolve
    // entities.
    var span = window.document.createElement('span');
    span.innerHTML = '&' + rp[0] + '; ';
    ch = span.childNodes[0].nodeValue.charAt(0);
  }
  ret += ch + rp[1];
 }

 return ret;
}



<xmltext.js: DOMをXMLテキスト化する>(クリックでソース参照)

// Based on
// <http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247>
var DOM_ELEMENT_NODE = 1;
var DOM_ATTRIBUTE_NODE = 2;
var DOM_TEXT_NODE = 3;
var DOM_CDATA_SECTION_NODE = 4;
var DOM_DOCUMENT_NODE = 9;
var DOM_DOCUMENT_FRAGMENT_NODE = 11;

// Returns the text value if a node; for nodes without children this
// is the nodeValue, for nodes with children this is the concatenation
// of the value of all children.
function xmlValue(node) {
 if (!node) {
  return '';
 }

 var ret = '';
 if (node.nodeType == DOM_TEXT_NODE ||
   node.nodeType == DOM_CDATA_SECTION_NODE ||
   node.nodeType == DOM_ATTRIBUTE_NODE) {
  ret += node.nodeValue;

 } else if (node.nodeType == DOM_ELEMENT_NODE ||
       node.nodeType == DOM_DOCUMENT_NODE ||
       node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
  for (var i = 0; i < node.childNodes.length; ++i) {
   ret += arguments.callee(node.childNodes[i]);
  }
 }
 return ret;
}


// Returns the representation of a node as XML text.
function xmlText(node) {
 var ret = '';
 if (node.nodeType == DOM_TEXT_NODE) {
  ret += xmlEscapeText(node.nodeValue);
  
 } else if (node.nodeType == DOM_ELEMENT_NODE) {
// midified by S.Takezaki(to LowerCase)
  ret += '<' + node.nodeName.toLowerCase();
  for (var i = 0; i < node.attributes.length; ++i) {
   var a = node.attributes[i];
   // midified by S.Takezaki
   if (a && a.nodeName && a.nodeValue && a.nodeName!='contentEditable' ) {  
    ret += ' ' + a.nodeName.toLowerCase();
    ret += '="' + xmlEscapeAttr(a.nodeValue) + '"';
   }
  }

  if (node.childNodes.length == 0) {
   ret += '/>';

  } else {
   ret += '>';
   for (var i = 0; i < node.childNodes.length; ++i) {
    ret += arguments.callee(node.childNodes[i]);
   }
// midified by S.Takezaki(to LowerCase)
   ret += '</' + node.nodeName.toLowerCase() + '>';
  }
  
 } else if (node.nodeType == DOM_DOCUMENT_NODE ||
       node.nodeType == DOM_DOCUMENT_FRAGMENT_NODE) {
  for (var i = 0; i < node.childNodes.length; ++i) {
   ret += arguments.callee(node.childNodes[i]);
  }
 }
 
 return ret;
}

// Escape XML special markup chracters: tag delimiter < > and entity
// reference start delimiter &. The escaped string can be used in XML
// text portions (i.e. between tags).
function xmlEscapeText(s) {
 return s.replace(/&/g, '&').replace(/</g, '<;').replace(/>/g, '>');
}

// Escape XML special markup characters: tag delimiter < > entity
// reference start delimiter & and quotes ". The escaped string can be
// used in double quoted XML attribute value portions (i.e. in
// attributes within start tags).
function xmlEscapeAttr(s) {
 return xmlEscapeText(s).replace(/\"/g, '"');
}

// Escape markup in XML text, but don't touch entity references. The
// escaped string can be used as XML text (i.e. between tags).
function xmlEscapeTags(s) {
 return s.replace(/</g, '<;').replace(/>/g, '>');
}


<関連>
 DOMをXMLに変換するjQueryプラグイン

月曜日, 7月 07, 2008

【EC開発体験記-View-】 ワンソースマルチビューを実現する このエントリーを含むはてなブックマーク


 Reflexフレームワークの真骨頂はViewである。Reflexは、ブラウザ、携帯、Flex2、PDFなど様々なViewに対して、同じEntityを与えさえすれば、同じように表示させることを目標とする。以前にも述べたが、1つのEntityから複数の写像をとれるという意味で、名前をReflex(写像)とした。これをワンソース・マルチビューというらしい。(xfy by ジャストシステムさん)
 本来は、EntityをマッシュアップするControlerがViewからの参照元なので、ControlerはViewを元に設計すればよいことになる。さらには、EntityもViewを意識することで、Viewのイメージを崩すことなく一貫して扱えるようになるので、このような設計を心がけるべきだろう。実際に私たちがプロジェクトで構築したシステムにおいては、理想とはいえないものの、一貫したEntityの利用が一応できている。例えば、Flex2のカートから受注プロバイダにXML送信された受注(Entity)は、管理画面のPHPからJSONリクエストで呼ばれることもあれば、納品書PDFとして出力されることもある。否定の3.を参照。
3.各サブシステムで共通のエンティティを使用(最小単位は1品1葉1レコード)

 それでも理想といえないのは、統一したレンダリング方法がないからである。一つのテンプレートを作成するだけで、Flex2やPDFなどの様々な表現に対応させたかったのだが、そこまではできておらず、それぞれのViewに応じた実装とスキルが必要であった。
 そこで、現在開発中のReflex Template(コードネームv:lex)では、XHTMLによるテンプレートを一つ作成するだけで様々な表現ができるようにした。考え方としては、下図のように、EXCELのようなスプレッドシートのイメージでViewを設計し、1枚のシートをdocument、ページをpage/tab、表をtableとして配置していく。tableはページに何個でも置くことができ、位置も細かく指定できる。なお、表題などの文章や画像などは1マスの表とする。
 Templateは動的に選択でき自由に画面遷移できる。また、TABを利用することで、遷移なし(ページ読込なし)に異なる画面を表示させることができる。セッションという考えはなく、Entityにデータを保存する。つまり、Entityはグローバルであり共有データとしても使う。Entityを与えてBlogicを実行するが、BlogicはクライアントJavaScriptでも、サーバのサービスでもどちらにあってもよい。



 また、以下のような5階層のレイヤーをもち、Template Engineによって各層が組み立てられる。


<Template Engineの機能>

1.Templateを選択する機能
- page id(xxxx.html#001)で指定
2.サーバからEntityを取得しBlogicを実行する
- Entityをチェックしてエラーコードをセットする。その後はAttributeによってエラーが表示される
- エラーがなければBlogicを実行する
3.静的なtemplateの上に動的なEntityをマッピングする。その際、Attributeにより値をフォーマットする(カンマを付けたり色を付けるなど)
4.データ入力時にvalidatorを実行する
5.(エラーが含まれていなければ)Entityをサーバに送信する


 一つだけ注意すべきことがある。それは、Entityへのデータ入力点(この場合、サーバから取得した時点とキー入力の時点)におけるエラーチェックを厳密にすることだ。キー入力の時点では、Validatorとして古くからJavaScriptは一般的に使用されてきたが、サーバから読込んだ時点というのは意外な方も多いだろう。この設計では、ここをチェックしないとハングする場合もあるので要注意だ。というわけで、否定の16はやはり間違いであった。

16.入力は寛大に受け付け、出力は厳しくせよ

 なお、Blogicにおいてエラーとなった場合には、エラーコードをスタックして返すようにする。エラーコードの種類により、Alertを出したり、色を変えたりするのはValidatorではなく、Attributeの責務である。
 詳細は完成してから発表したい。乞うご期待。

日曜日, 7月 06, 2008

【雑記】 MapReduce HTTPMR、Groovy、Flashインデクス化など このエントリーを含むはてなブックマーク


これまで概念だけが先行していたWeb2.0。ここにきて実装技術が続々と登場してきている。そこで、最近の気になる記事をピックアップして、レイヤー(View、Mashup、Resource)ごとにまとめてみた。レイヤーについては、これを参照いただきたい。 

まず、View層のトピックでは、ちょっと地味かもしれないが・・Flashのインデクス化。小川さんのコメントケータイに及ぼす影響が大きいという意見。iPhone登場もあり、今後のITビジネスの中心はケータイになると思われるので同意。

次に、Resource層では、MapReduce HTTPMRの登場。
HTTPベースによるMapReduceフレームワーク「HTTPMR」
MapReduceについては、ここや、ここでも触れたが、インターネット分散環境におけるデータの扱いということで、レスポンスが返らなくてもよしとするとか、狂ったデータを無視するとか、ちょっと特殊なテクニックを要する。

Groovyは、Mashupにおける実装技術の一つ(と強引に考えたのは、sMashで採用されているからに他ならない)Groovyについては、NetBeans 6で学ぶGroovyとGrailsがわかりやすい。

 上記は特にSaaSにとっての朗報かと思う。高い生産性をもたらすスクリプト言語がJavaに影響した結果Groovyができた。HTTPMRに関して言えば、Google App Engineを活用しており無償で動作する。サーバ管理がある以上、Cheap革命はオープンソースを使うだけでは成し遂げられないが、Google App EngineやS3などは一つの解でもある。一方、個人情報の扱いなどの制約があり、エンタープライズでどう活用していくかが課題である。

金曜日, 7月 04, 2008

【本日の嵌り】 Google八分? このエントリーを含むはてなブックマーク


先日のsMashの記事がGoogle八分にあったようだ。単なるランク落ちと思われがちだが見分け方はある。
googleでinfo:をつけて検索すればよい。以下の2つの違いは見てみると分かるが、ibm-smash.htmlでは、「~に関する情報は見つかりませんでした。」と出る。


info:http://www.virtual-tech.net/blog/2008/07/javascript-log4js.html
info:http://www.virtual-tech.net/blog/2008/06/ibm-smash.html



Google の サイトの登録のページ で、もう一度登録してみるといいとのこと。

これで解決。

木曜日, 7月 03, 2008

【JavaScript】 ローカルに保存する機能をもつLog4js このエントリーを含むはてなブックマーク


 Log4jsのFileAppenderを発見。なるほど、ローカルに書き出しはこうすればよいのか。
こちらで、富田さんがブラウザからのローカル保存に触れられていたので興味はあったけど、今までやり方が分からなかった。ちなみに、こんなこともできるらしい。
業務系Webアプリでファイルアップロード後にローカルファイルを削除する


Log4js.FileAppender = function(file) {

  this.layout = new Log4js.SimpleLayout();
  this.isIE = 'undefined';
  
  this.file = file || "log4js.log";  
  
  try{
    this.fso = new ActiveXObject("Scripting.FileSystemObject");
    this.isIE = true;
  } catch(e){
    try {
      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
      this.fso = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
      this.isIE = false; //mozilla & co
    } catch (e) {
      log4jsLogger.error(e);
    }
  }
};


Log4jsはシンプルで実によくまとまっている。prototype.jsからコピーしたと思われる、継承(extend)、bind、それからattachEventがあるだけだ。実際これで基本的なことは大抵できる。
$()も放棄してシンプルに実装している点も感心である。JavaScriptアプリはライブラリをインクルードしないでコンパクトにする、こういったコピペスタイルが流行ってくるのではないだろうか。
追記:というわけで、短いライブラリを作ってみた。=>とっても短いAJAXライブラリ extractive.js


/**
* 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]);
  };
}

/**
* Define bind if no prototype available.
* @private
* @ignore
*/
if(!Function.prototype.bind) {
  /**
   * Functions taken from Prototype library,
   * didn't want to require for just few functions.
   * More info at {@link http://prototype.conio.net/}
   * @private
   */  
  Function.prototype.bind = function(object) {
   var __method = this;
   return function() {
    return __method.apply(object, arguments);
   };
  };
}

  /**
   * 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
   */
  attachEvent: 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);
    }
  }


【JavaScript】 StyleをセットできるBuilder.node() このエントリーを含むはてなブックマーク


scriptaculousのbuilder.jsとほぼ同じ機能だが高速。一括してエレメント(タグ)と属性や内容を指定することができる。また、classNameを指定できるほか、StyleをActiveにゲット、セットできるのが拡張点。
追記:これはname.camelize()を使っており、prototype.jsを必要とする。必要のないバージョンはこちらにある=>とっても短いAJAXライブラリ extractive.js

var Builder = {

   initialize: function() {
   },
   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[name.camelize()] = 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 "";
   }
}


top変数により、styleに動的にセットする


var hoge = Builder.node('div', { id : hogeid, className : 'hoge', style : { marginTop : top+'px'} } );



水曜日, 7月 02, 2008

【本日の嵌り】 DNS MXレコード このエントリーを含むはてなブックマーク


Webサーバにメールサーバと同じドメイン名を使ってしまった。これではメールが全部、Webサーバに飛んでいってしまう。Webサーバのメール設定はしていないので、お客様の問い合わせを受け付けられないし、メールの注文もできない。><どうしよう・・・・。

http://example.comと、hoge@example.com

あ、そうだ。DNSのMXレコードがある。

MXレコードを追加すれば別のサーバに配信できる。また、優先度を決めることができ、送信できなかった場合に順番に送信される。ちなみに、WebサーバにはCNAMEをつかうと便利。

これで解決。


example.com. IN A 192.168.1.2
example.com. IN MX 10 mail.example.com.


<参考>
nslookupの基本的な使い方(MXレコード編)
メール/Webサーバを効率的に動かすゾーン設定
CNAMEレコードの使い方

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