[mayaa-user:1001] Re: builderをカスタマイズしてexec processorを自動挿入したい
Susumu ISHIGAMI
[E-MAIL ADDRESS DELETED]
2014年 2月 8日 (土) 16:17:42 JST
石上です。
sugaさんご回答ありがとうございます。
>これだけ読むと Mayaa(一般にはjsp)に渡す前にServletで全部結果をまとめておくのが良いのでは、と思うのですが。
>全部のデータを持つのが現実的では無いサイズの場合はキャッシュできないでしょうし。
まずは前提が省略されていたことをお詫びします。
せっかくですので、公開可能な範囲で状況を共有致します。
今回扱っているWebアプリでは、開発者とテンプレート記述者が異なる場所で働いており、
テンプレート記述者はよりエンドユーザーに近い立場の方が担当されています。
テンプレート記述者は「汎用的な」制御を開発に求め、自由にページを構築しています。
そのため、開発者が想定外の使い方をテンプレート記述者がしてしまうことがあり得ます。
今回は機能要件は満たすがパフォーマンスが悪いテンプレートが作られてしまいました。
具体的には、あるidを使用するとDBからデータをロードし、そのタグの子要素で
ロードしたデータが使えるというものを提供したのですが、そのidをページ内で150以上使用されてしまい、
キャッシュなどがない初期表示時に150本のSQLが発行されてしまう問題がありました。
そこでテンプレート上部に必要なレコードのIDを列挙して頂くことを打診しましたが、
頻繁にページを変更したいということで、デザインを変更するごとに、ページ上部の記述を変更することは
避けたいという希望を受け、ページ内のデータを自動で解析することを考えました。
その方法としてbuilderをカスタマイズし、ビルド時にテンプレートの構造データをカスタマイズすることを考えました。
>描画時にはビルドの結果出来上がる TemplateImpl が持つ TemplateProcessor のツリーだけを使うので、
>そこに破綻がなければ問題ありません。
>
>ただ、元のテンプレートの情報を使うような Processor (echoなど)には
>対応する値をセットしておかないと正常に動作しないことがあるでしょう。
ご説明ありがとうございます!
・レンダリング時にはTemplateProcessor のみを使用する
つまり、
template.insertProcessor(0, proc);
さえ正常に行えれば、自由にテンプレートの処理をカスタマイズできる
・元のテンプレート情報を使うProcessorは対応する値のsetが必要
proc.setOriginalNode(htmlNode);
proc.setInjectedNode(exec);
この部分を指しているのではないかと思います。
InjectionResolverとTemplateBuilder(SpecificationBuilder)の役割分担が理解できていなかったのですが
BuilderはXMLファイルをパースするところから始まって、TemplateProcessorのツリーを生成するのがゴールであって、
InjectionResolverやSpecificationNodeはその過程で作られるということだと理解しました。
TemplateBuilderImplのafterBuildでdoInjectionが行われることによって、
SpecificationNodeからTemplateProcessorツリーが作られるので、
TemplateBuilderImplをカスタマイズする場合で、SpecificationNodeをいじる場合は
super.afterBuildの前に行うべきであり、Processorのツリーを直接いじるならどちらでも構わない。
そんなところでしょうか。
TemplateBuilderImplのソースを読ませて頂いていると、
SAXを使ってXMLをパースしているところまで見つけられましたので、
Mayaaの内部ロジックに近づけたことに感動を覚えました。
2014年2月6日 20:38 suga <ko.suga @ gmail.com>:
> suga です。
>
>> よくわかっていないことが、SpecificationNodeとProcessorは一対一に
>> しなければならないのかということです。
>
> 基本的には、SpecificationNode は Processor のインスタンスを作るための指示なので、
> 通常のビルドプロセス後であれば何かを追加したところで Mayaa 側からは何もしません。(できません)
>
>> 例えば今後このような対応を複数重ねあわせた時、
>> 後発の処理が同じhtmlタグにプロセッサーを上書きしてしまうのでしょうか。
>> それとも、insertChildNodeで、テンプレートにダミーのタグを追加する方が良いでしょうか?
>
> それは「後発の処理」次第ですね。
> TemplateBuilder#afterBuild であればそのテンプレートに対する Mayaa の処理は
> 終わっているので、「複数重ね合わせ」る処理がどうするかです。
>
>
>> このような書き方は問題ありませんでしょうか?
>
> これも以下同文で、拡張したビルド処理の後にさらに拡張するのであればそれ次第、というところです。
>
> 描画時にはビルドの結果出来上がる TemplateImpl が持つ TemplateProcessor のツリーだけを使うので、
> そこに破綻がなければ問題ありません。
>
> ただ、元のテンプレートの情報を使うような Processor (echoなど)には
> 対応する値をセットしておかないと正常に動作しないことがあるでしょう。
>
>
>
> おそらく前提が省略されているだけだと思いますが、
>
>> 具体的には画面で何回も細切れにlazyローディングなデータのselectを発行してしまって遅いので、
>> 最初にまとめてがっつり取ってきてキャッシュしておきたいという場面があります。
>
> これだけ読むと Mayaa(一般にはjsp)に渡す前にServletで全部結果をまとめておくのが良いのでは、と思うのですが。
> 全部のデータを持つのが現実的では無いサイズの場合はキャッシュできないでしょうし。
> --
> suga ( ko.suga @ gmail.com )
>
>
> 2014-02-03 Susumu ISHIGAMI <susumu.ishigami @ gmail.com>:
>> 石上です
>>
>> そのまま処理をすると遅くなってしまうテンプレートがあり、
>> builderを改良してソースコードを解析した上で、
>> 事前にテンプレート定義を書き換えてしまって高速化しようと考えました。
>>
>> 具体的には画面で何回も細切れにlazyローディングなデータのselectを発行してしまって遅いので、
>> 最初にまとめてがっつり取ってきてキャッシュしておきたいという場面があります。
>> 既に実装はほぼできていて、次のような方式で実現しました。
>>
>> TemplateBuilderImpl
>> をextendした独自クラスで、
>> afterBuildをOverrideし、specificationの中身を解析した上で、
>> 動的にscriptを生成し、execプロセッサーを強制的に追加することで、
>> そのscriptが実行されると適切にキャッシュが作られて
>> 後続の処理が高速化される
>>
>> ここで、execプロセッサーを動的追加するために以下の様なコードを
>> 書きました。(インデントを見やすくするためタブを全角スペースに変換しています)
>>
>> static void injectHeaderScript(Specification specification, String script) {
>> Template template = (Template) specification;
>> QName qName = QNameImpl.getInstance("exec");
>> LibraryManager libraryManager = ProviderUtil.getLibraryManager();
>> ProcessorDefinition def = libraryManager.getProcessorDefinition(qName);
>> SpecificationNodeImpl exec = new SpecificationNodeImpl(qName);
>> exec.addAttribute(QNameImpl.getInstance("script"), "${" + script + "}");
>> SpecificationNode htmlNode = getHtmlNode(template);
>> TemplateProcessor proc = def.createTemplateProcessor(htmlNode, exec);
>> if (htmlNode != null) {
>> proc.setOriginalNode(htmlNode);
>> proc.setInjectedNode(exec);
>> template.insertProcessor(0, proc);
>> }
>> }
>>
>> static SpecificationNode getHtmlNode(NodeTreeWalker current) {
>> Specification specification = SpecificationUtil.findSpecification(current);
>> for (Iterator<?> it = specification.iterateChildNode(); it.hasNext();) {
>> SpecificationNode node = (SpecificationNode) it.next();
>> if (node.getQName().getLocalName().toLowerCase().equals("html")) {
>> return node;
>> }
>> }
>> return null;
>> }
>>
>> よくわかっていないことが、SpecificationNodeとProcessorは一対一に
>> しなければならないのかということです。
>>
>> 例えば今後このような対応を複数重ねあわせた時、
>> 後発の処理が同じhtmlタグにプロセッサーを上書きしてしまうのでしょうか。
>> それとも、insertChildNodeで、テンプレートにダミーのタグを追加する方が良いでしょうか?
>>
>> また、htmlタグを探さずに、単に
>> SpecificationNode node = (SpecificationNode) template.getChildNode(0)
>> TemplateProcessor proc = def.createTemplateProcessor(node, exec);
>> としても動きました。
>> この場合、nodeはDOCTYPEノードになるようです。
>> このような書き方は問題ありませんでしょうか?
>>
>> マニアックで申し訳ありませんが、
>> builderを書き換えてprocessorやnodeを操作できると、面白いことができそうです。
>> _______________________________________________
>> mayaa-user mailing list
>> mayaa-user @ ml.seasar.org
>> https://ml.seasar.org/mailman/listinfo/mayaa-user
> _______________________________________________
> mayaa-user mailing list
> mayaa-user @ ml.seasar.org
> https://ml.seasar.org/mailman/listinfo/mayaa-user
--
Susumu ISHIGAMI
susumu.ishigami @ gmail.com
mayaa-user メーリングリストの案内