アセットのデータ取得方法によるパフォーマンス測定(その2)

前回:アセットのデータ取得方法によるパフォーマンス測定(その1)

前回に引き続き、今回も、アセット(フレックスファミリーアセット)のデータを取得するのに、より速い取得の方法はないか調べてみたいと思います。

今回は、Asset APIがどのくらい速いのか、Asset APIと、assetset:getmultiplevaluesタグで測ってみます。

前回と同様、アセット10コ、50コ、100コで計測してみましょう。
データの取得は、アセットがもつ属性から適当に20コ(すべて単一)を選んでみます。

では、テスト用のテンプレートをそれぞれ作ります。

1)Asset API

List listAttrNames = new ArrayList();
//20コ取得
listAttrNames.add("testA");
listAttrNames.add("testB");
listAttrNames.add("testC");
listAttrNames.add("testD");
listAttrNames.add("testE");
listAttrNames.add("testF");
listAttrNames.add("testG");
listAttrNames.add("testH");
listAttrNames.add("testI");
listAttrNames.add("testJ");
listAttrNames.add("testK");
listAttrNames.add("testL");
listAttrNames.add("testM");
listAttrNames.add("testN");
listAttrNames.add("testO");
listAttrNames.add("testP");
listAttrNames.add("testQ");
listAttrNames.add("testR");
listAttrNames.add("testS");

Session ses = SessionFactory.getSession();
AssetDataManager mgr = (AssetDataManager) ses.getManager(AssetDataManager.class.getName());

%><ics:if condition='<%=ics.GetList("listTests")!=null && ics.GetList("listTests").hasData()%>'><ics:then><%
 %><ics:listloop listname="listTests"><%
 %><ics:listget listname="listTests" fieldname="id" output="o_AssetId"/><%

String baseAssetId = ics.GetVar("o_AssetId");
 AssetId id = new AssetIdImpl( "TestAsset_C", Long.parseLong(baseAssetId));
 Iterable<AssetData> dataItr = mgr.read( Collections.singletonList( id ) );

for( AssetData data : dataItr )
 {
 for(int y=0; y<listAttrNames.size(); y++) {
 AttributeData atrData = data.getAttributeData((String)listAttrNames.get(y));

}
 }

%></ics:listloop><%
%></ics:then></ics:if><%

2)assetset:getmultiplevaluesタグ
前回と同様ですので省略します。前回はこちら

測定開始です。

・・・測定・・・測定・・・測定・・・。

出ました。結果です。

アセット数

取得属性数

AssetAPI

assetset:getmultiplevaluesタグ

10

20

735ms

84ms

50

20

5437ms

353ms

100

20

9085ms

869ms

※動作環境や定義がもつ属性の数によって変動はあります。正確な数値ではありません。
※同じアセットタイプ、同じ定義で、すべて異なるアセットを使っています。
※フレックスアセットの参照はassetset:setassetタグを使っています。
※3回実行し、その平均値になります。

結果に驚きました。桁が違いますね。assetset:getmultiplevaluesタグが速いというより、Asset APIが遅いです。よくわかったことは、アセットデータを取得するのにAsset APIを使っては危険ということです。管理サイトのカスタマイズなど、管理側の機能に限定して使ったほうが良いかもしれません。

ということで、季節は春です。暖かくなってきました。春に向けての準備など、やりたいことがいろいろありますね。会社では新入社員が入ってくるので新人教育の準備、外では桜の花が咲き始めているので花見の準備、家では車の冬用タイヤをノーマルタイヤへの交換、あ、それは私だけですね。
花見ですが、東京では3月末に開花しているので、今週は絶好の美味しいお酒を味わえそうですね。

sakura_L


【新機能】Vanity URLを試してみる(その2)

前回:【新機能】Vanity URLを試してみる(その1

 前回 からだいぶ空いてしまいましたが、Vanity URLその2ということで書かせていただきました。代わりに書いてくれる人を探しましたが誰もいなかったので、このブログは担当が独自に調査して書いています。多少間違っていても温かい目で見守って頂けますと幸いです。

前回の「その1」では、アセットごと(ページ単位とも言えます)でのVanity URLの設定について試してみました。今回はアセットタイプ毎のVanity URLの設定を試してみようと思います。

同じアセットタイプのアセットを用いて表示するページのURLをあるパターンに従って自動生成してみます。

前回同様、JSKのサンプルサイトFirstSiteIIを用いて検証していきます。

※その1の公開(2013年12月)から3ヶ月ほど経ちましたが、その間にドキュメントも日本語版が公開されています。

http://docs.oracle.com/cd/E51625_01/doc.1111/e49682/config_vanity_url.htm#i1093062

1. 自動生成されるVanity URLを設定する箇所

Vanity URLの自動生成はアセットタイプ毎(または定義毎)に設定します。よって、同じアセットタイプのアセットをプライマリとして表示するページが複数存在する、一覧表示を行うようなページで設定するのが良いと思われます。

今回はサンプルサイトFirstSiteIIのHot Productsのページから各製品の詳細ページへ遷移するURLにVanity URLを設定してみます。

http://localhost:9080/cs/Satellite?c=Page&childpagename=FirstSiteII%2FFSIILayout&cid=1124747609912&pagename=FSIIWrapper

vanity_02_01

一覧の一番上の「FSE Digital Audio Player」のリンクURLは次の通りとなっています。

http://localhost:9080/cs/Satellite/FirstSiteII/FSII/Product_C/1114083739350/1124747609912?packedargs=locale%3D1154551493541

このリンクはプライマリアセットがProduct_Cとなり、その下の「FSII Innovative HiFi VHS」のリンクURLも同様のパターンとなっています。よって、Product_CのアセットタイプにVanity URLを設定してやることで、この一覧の製品詳細ページのリンクURLにVanity URLの自動生成が適用できます。

現状、このURLにはURLアセンブラが設定されていますので、クエリのみで組み合わせたわかりづらいURLではありませんが、今回はこのURLにVanity URLを設定し、両方設定した場合はどうなるか、ということも確認してみます。

ちなみに上記のURLでアセンブラが設定されていない場合のURLは以下の通りとなります。

http://localhost:9080/cs/Satellite?c=Product_C&childpagename=FirstSiteII%2FFSIILayout&cid=1114083739350&p=1124747609912&packedargs=locale%3D1154551493541&pagename=FSIIWrapper

2. 自動生成URLの構成

Vanity URLの自動生成を構成します。上記のURLをみて分かる通り、プライマリアセットのタイプは「Product_C」であるためそのアセットタイプに対して設定を行います。

管理画面のAdmin UIから「管理」タブを選択し「アセット・タイプ」→「Product_C」→「URLパターン」を選択します。

そこで「新規追加」をクリックします。

vanity_02_02

Asset/Blob Product_Cのアセットであるため、Assetを選択します。(Blobは画像やバイナリファイルの場合に用います。)
サイト FirstSiteIIのままとします。
ホスト その1で用いたものと同じWebrootの「http://localhost:9080/cs/fsii」を選択します。
サブタイプ アセットのサブタイプ(定義)を絞り込む場合に用います。今回は任意とします。
デバイスグループ 今回はそのままとします。
Template そのままとします。
ラッパー FSIIWrapperとします。
パターン 今回はシンプルに次の通りとします。
Product_C/${name}.html
${name}のところはアセット名が入ります。おそらくアセットを特定するための変数を組み込む必要がありますが、右側の表で表示される変数名が使えます。また簡単な関数も使うことができます。

最後に保存を行います。

次に実際の表示アセットProduct_Cを参照します。

Contributor UI にて、「FSE Digital Audio Player」のアセット(1114083739350 )を参照します。

vanity_02_03

既存のアセットタイプに対してVanity URLの自動生成を設定したため、実際にVanity URLの設定を反映させるために、アセットの再保存を行います。

鉛筆マークから編集ページを表示し、そのまま保存します。

その後、URLタブを表示します。

vanity_02_04

Vanity URLが自動生成されています。

今回はすでに作成済みのアセットについて後から自動構成を設定したため、アセットの再保存を行いましたが、アセットタイプに対してVanity URLの設定を行ってから新規作成する場合は再保存の必要はありません。

同様の処理を「Innovative HiFi VHS 」アセットID: 1114083739696のアセットについても行います。

設定が終わったら、再度Hot Productsのページを開いてみます。

vanity_02_05

FSII FSE Digital Audio Player のリンクURLは

http://localhost:9080/cs/fsii/Product_C/FSII+FSE+Digital+Audio+Player.html?p=1124747609912&packedargs=locale%253D1154551493541

となりました。無事にVanity URLが設定されました。

クエリの部分を除くと、自動生成のパターンとして設定した

Product_C/${name}.html

の通りとなっています。

また、その下のFSII Innovative HiFi VHSのリンクURLもVanity URLで設定したパターンになっています。

URL中のpと、packagedargsのクエリパラメータはURLパターンに組み込まれないため、そのままクエリ文字列として出力されます。クエリパラメータがなくなってしまうといった心配もありません。

3. Vanity URLとURLアセンブラ

Vanity URLをひと通り試してみましたので、Vanity URLとURLアセンブラを私なりに比較してみました。

  • 通常URL
http://localhost:9080/cs/Satellite?c=Product_C&childpagename=FirstSiteII%2FFSIILayout&cid=1114083739350&p=1124747609912&packedargs=locale%3D1154551493541&pagename=FSIIWrapper
  • URLアセンブラ(fsiiの場合)
http://localhost:9080/cs/Satellite/FirstSiteII/FSII/Product_C/1114083739350/1124747609912?packedargs=locale%3D1154551493541
  • Vanity URL
http://localhost:9080/cs/fsii/Product_C/FSII+FSE+Digital+Audio+Player.html?p=1124747609912&packedargs=locale%253D1154551493541

上記からも分かる通り、URLアセンブラの場合は、アセットタイプ、アセットIDと、また、p=で渡しているID(これはPageアセットのIDですが)もURLの一部として組み込まれています。p のパラメータ名はURL中に出てきませんので、こちらは変換ロジックで決定されているものと思われます。

Vanity URLの方はProduct_CのアセットIDがURL中に含まれず、代わりにアセット名が出力されています。PのIDはクエリとして付加されています。/Product_C/ の部分も別の名称に変えることもできると思います。

このパターンは管理画面から自由に設定できますがおそらくアセットを識別するための変数はひとつ以上は含める必要があるのではないかと思います。

Vanity URLの場合のメリットとデメリットについて考えてみました。

Vanity URLはアセット(個別、もしくはアセットタイプごと)に設定を行うため、一つのアセットタイプに関する情報しかURLに組み込むことができないと思います。それ以外に渡されるパラメータをURL中に含めることはおそらくできません。これはデメリットですが、逆にシンプルでわかりやすいと思います。

URLの設定は管理画面上から行うことができ、運用担当者でも任意にURLを設定できるというのが大きなメリットかと思います。変換後のURLを管理画面で確認することもでき、また管理タブのシステムツールから現在設定されているURLの一覧を確認することができます。

URLアセンブラに関しては、ここでは詳しくは触れませんが、変換ロジックを独自に実装する必要があり、管理画面上でVanity URLのように編集が必要であれば、管理画面側のカスタマイズも必要となるかもしれません。また変換パターンに関しても、どこかに保持しておく必要があるため、その方法についても考える必要があります。

その代わり、独自に実装できるため、URLの自由度は高くなります。複数のアセットIDを組み合わせたり、任意の文字列を組み込んだりといったことが実装次第で可能となります。その分、複雑になるため運用者が管理するのは難しくなる可能性はあります。開発者側で管理する必要が出てくるかもしれません。

最後に、簡単にまとめますと、

Vanity URLは扱いが簡単であるが、設定できるURLにはある程度制限がある。標準で準備されているため運用者側でも扱いやすい。(ある意味URLアセンブラの実装の一つとも言えるかもしれません)

URLアセンブラはURLの自由度は高い(実装次第)が、標準機能として用意されていないため作りこみが必要。運用を考慮して作りこむ必要があり、場合によっては開発サイドでメンテナンスが必要となる。

どちらにもメリット、デメリットはあると思いますので、場所によって使い分ける、といった使い方がよいかもしれません。

※上記の記述は担当が調査した範囲での記述となります。その正確性、完全性についてはジークス社は責任を負いません。

WCSのキャッシュは夜ひらく

 みなさん、こんにちは。毎日寒い日が続きますね。大雪の被害にあわれた方々には、心よりお見舞い申し上げます。
東京も記録的な大雪でした。幸い週末にかかったため、大きな混乱はなかったようですが、未だ山間部では道路や線路が不通のところがあるようです。
※東京に山間部なんてあるのかよ!って方は一度、Googleマップなどで東京の奥多摩あたりをご覧になってください。ちなみに、最高峰は奥多摩の雲取山(標高2017m)です。(筆者も初めて知りました!)

 今回は、キャッシュのお話です。動的配信を行っている場合、キャッシュはクライアントからのアクセスに素早く反応したり、サーバの負荷を軽減するために非常に有効な手段ではありますが、その使い方を間違えると大変なことになります。
以下にキャッシュを使う上での問題点を挙げてみます。

1. ページのファーストアクセスが遅い問題
パブリッシュ時にキャッシュを再生成しない場合は、キャッシュの有効期限が切れたあと、最初のアクセスが発生した段階でキャッシュを生成することになります。つまり、アクセスする誰かがババを引くことになるのです。
もちろん、パフォーマンスとしての要件を満たすようページ出力に要する時間は、厳しくチェックされチューニングされていますが、それでもキャッシュが有効な場合に比べれば、時間がかかることには違いはありません。
一般的なWCSの動的配信の案件では、概ね3秒以内のレスポンスを目指してチューニングが行われていますが、それでもページの作り上、より時間がかかったり、コンマ数秒で表示されるキャッシュ有効時の表示よりは、はるかにレスポンスが遅い結果になります。

2. パブリッシュ終わらない問題
WCSではパブリッシュ時にキャッシュを自動再生成する機能があります。
これを使えば、パブリッシュした資材に関して関連するページのキャッシュを再生成することができます。
これを使えば、たまたまファーストアクセスに当ってしまう人もなく、すべての問題が解決できるのではないか?と一瞬思ってしまいますが、そこに落とし穴があります。

 キャッシュを再生成するには時間がかかるということです。自動で再生成するとはいえ、キャッシュを生成するにはそれなりの時間がかかります。
例えば、製品が数千あるサイトにて、製品を1つ追加したとします。その製品にたどり着くための中間的なカテゴリページに製品数の表示などがあったとしたら、製品を1つだけ追加したとしても、その中間経路のページの製品数の表示をすべて直さなければなりません。その経路自体が数百もあったとしたらどうでしょうか?
製品を1つだけパブリッシュしたのに、キャッシュの自動再生成に時間がかかりパブリッシュがいつまで経っても終わりません。そんな結果になってしまいます。

運用に合わせたキャッシュルールを考える!
 以上の問題を解決するためには、運用に合わせたキャッシュルールを策定する必要があります。
いくらパブリッシュが早くても、キャッシュ率の低いサイトはアクセスが遅くなりますし、いくらキャッシュ率が高くても、運用時間中のパブリッシュに時間がかかりすぎては運用に支障を来します。
例えば、サイトの構成、運用方法や要望、アクセスするクライアントの性質が以下の通りだったとします。

  • 製品情報の詳細は多数の経路を辿り表示され、それぞれの経路に配下の製品数やスペックの一部が表示される。
  • 運用時間は、概ね通常の勤務時間(9:00~18:00)。
  • サイトへのアクセスもB to Bのため、9:00~18:00の間のアクセスが殆どを占める。(夜間のアクセスが少ない)
  • 運用時間中は、製品情報の更新も頻繁に行う上、重要なお知らせが突発で入る場合もあるのでパブリッシュ時間はなるべく短くしたい。
  • キャッシュ率は極力100%に近づけたい。
  • 製品情報は特別な場合を除いて、次の日にすべての導線が繋がり、導線に表示される製品数なども表示されればよい。

以上の条件を踏まえ、以下のようなスケジュールを構築します。

運用スケジュール
製品ページツリー
 製品詳細に至る複数の経路のページ(製品中間A~E)のキャッシュを、アクセスの少ない夜間に再生成することによって、クライアントのアクセスの負担を軽減し、通常運用時のパブリッシュ時間を短縮し、キャッシュ率を極力高くすることを実現します。
 キャッシュはサイトへのアクセスを高速化する素晴らしい手段ですが、キャッシュは作らないと存在しない、作るためにはそれなりの時間がかかるということを忘れずに、サイト運用に則したキャッシュルールやスケジュールを策定することがWCSでの開発では非常に重要になります。

 今回のタイトルは…わかる方だけわかっていただければ幸いです。


入力欄をカスタマイズする!属性エディタ

ゆるゆるアニマルコラム担当のエンジニアKです。今回は「属性エディタ」についてです。新米PGのリス田くんはあれから成長したのでしょうか。それとも…?

◆登場人物紹介◆
mega02
メガネ先輩
ベテランSE。数々のWCS案件を経験してきたツワモノ。

risu02
リス田
新米プログラマー。WCSについて学び始めたばかり。
risu08 げへへー
mega02
risu08 うふふー
mega06 ???
mega06 …おい、リス田。何よだれ垂らしながらニヤニヤしてんだ。気持ち悪いぞ…。
てか、課題はちゃんと進んでいるのか?
risu02 ハッ!先輩っ!?課題ですか?
い、今まさに進めていたところですよー!
見て下さい、僕の…、WebCenter Sites牛丼屋メニューサイトを!!

2117_02_img01

mega02 …ほう。メニュー一品一品をアセット化したのか。
管理画面側はどんな感じ?
risu06 こんな感じです!

2117_02_img02

mega06 こ、これは…。
リス田、お前…、この入力画面、何か感じないのか??
risu07 何か…?
あっ!!サイドメニューも必要ですね!僕、健康志向なんでサラダとか付けちゃいますよー!
mega07 ばかもん!!貴様自身の健康活動なんぞどうでもいい!!
入力画面の入力欄を見てみろ!全部一行テキストボックスだろうが!
これじゃ使い勝手良くないわ!
risu04 ヒ、ヒィッ!!す、すいません!
って、入力欄をテキストボックス以外にする方法ってあるんですか?
mega04 お前、開発者ガイドちゃんと読んでないだろう…。
risu04 ウッ!
mega02 開発者ガイド「属性エディタの設計」に、入力欄の形を変える方法がちゃーんと載っているんだ。
属性エディタを使えば、ラジオボタンやチェックボックス、テキストエリア等々、入力欄を別の形に変える事が出来るぞ。
例えば「肉の種類」は牛肉・豚肉・鶏肉しか入力させたくない場合は、選択肢がその3つのラジオボタン項目を作ってしまえばいい。
属性エディタは以下の様なXMLを使って定義するんだ。

<?XML VERSION="1.0"?>
<!DOCTYPE PRESENTATIONOBJECT SYSTEM "presentationobject.dtd">
<PRESENTATIONOBJECT NAME="Radio_MeatType">
<RADIOBUTTONS LAYOUT="VERTICAL">
<QUERYASSETNAME>Query_MeatType</QUERYASSETNAME>
</RADIOBUTTONS>
</PRESENTATIONOBJECT>

mega01 XMLの内容を簡単に説明すると、Query_MeatTypeという問い合わせアセットの結果を用いて、ラジオボタン項目を縦方向(VERTICAL)に並べる、という事だな。
もちろん、問い合わせアセットは、「牛肉」「豚肉」「鶏肉」の3件が結果として返ってくるようにする。
risu02 おぉー。
mega02 「トッピング」なんかも、トッピングできる食材でチェックボックスとかにしてしまえばいい。
何でもかんでもトッピングできる訳じゃないだろう?
チェックボックスの場合の属性エディタはこんな感じかな。ラジオボタンの場合とよく似ているね。

<?XML VERSION="1.0"?>
<!DOCTYPE PRESENTATIONOBJECT SYSTEM "presentationobject.dtd">
<PRESENTATIONOBJECT NAME="CheckBox_Topping">
  <CHECKBOXES LAYOUT="VERTICAL">
    <QUERYASSETNAME>Query_Topping</QUERYASSETNAME>
  </CHECKBOXES>
</PRESENTATIONOBJECT>

risu01 なるほど…。
mega02 実際の案件だと、お客様の要望を聞いて、その要望に合った形・選択肢の入力項目を作成するんだ。
とりあえず、今の入力画面にいろんな属性エディタを適用させて、使い勝手の良い入力画面にしてみろ。
…あ、そうそう、出来るまでは昼飯に行っちゃだめだぞー。
risu04 な、なんですと!!そりゃ全力でやらねば!!

◇◇数時間後◇◇

risu09 先輩、出来ました…。
は、早く飯…昼飯を…。
mega01 ん?ああ遅かったなぁ。ちょっとまって。今チェックするから。
risu09 空腹時に食べ物関係の作業するのって辛いですね…。

2117_02_img03

mega01 ふむ。いい感じだね。
ラジオボタン、チェックボックスや、プルダウンも盛り込まれているな。よしよし。
アセットの設計をするときは、使う時の利便性までちゃんと考えた上で設計するのがポイントね。
risu06 はい!先輩わかりました!はい!先輩昼飯を食いに行きましょう!!
僕の頭の中はもう牛丼でいっぱいです!!牛丼食べに行きましょう!牛丼!!
mega05 えー。俺魚が食べたい気分なんだにゃ。
risu04 ガーン!!

以上、「入力欄をカスタマイズする!属性エディタ」でした!
次回をお楽しみに!


Content-Typeを動的に指定することは可能なのか

今回は、前回のマネージャーKのコラム(ページは見つかったのか。見つかっていないのか。)でちょっとだけ話に挙がった「Content-Type」についてお話します。

Webcenter Sites(以下WCS)では、Content-Typeをテンプレート作成時に指定することができます。

Content-Typeはテンプレートの設定項目の1つである、ページレットパラメータで”cs.contenttype”をキーに指定します。

ページレット

しかし、このページレットパラメータで指定したcs.contenttypeの値は、そのテンプレートで常に固定となるため、動的にContent-Typeの変更ができません。
サーブレットプログラムでは、下記コードのようにContent-Typeを動的に変更できます。

response.setContentType("text/html");

WCSのテンプレートでこのコードを書いてもエラーは発生しませんが、有効にもならないのです。

それでは、動的にContent-Typeを変更したい要件が出たときにはどうするべきでしょうか。

実際に携わった案件では、使われる可能性のあるContent-Typeのテンプレートを全て用意し、アクセスに応じて適切なContent-Typeのテンプレートを呼び出す方法を取りました。

例えば、gifへのアクセスであれば、cs.contenttypeが”image/gif”のテンプレートを呼出し、pdfへのアクセスであれば、”application/pdf”のテンプレートを呼び出すといった感じです。

呼出し方は色々な方法がありますが、Apacheのrewriteを使うのも一つの方法です。

Rewriteを使ったテンプレート呼出しイメージ

rewrite_image

もう一つ、こちらはまだ検証前ではありますが、テンプレートを”XML”タイプで開発することでContent-Typeを動的に変更できる可能性があります。

XMLにはHTTPのヘッダーを設定するタグ(ICS.STREAMHEADER)が用意されていて、このタグを使うことによってContent-Typeの指定が可能だということです。

実際にこのタグが有効かどうかは次の機会に検証したいと思います。

今のところ、JSPでの開発ではContent-Typeを完全な動的化にする方法が見つかっていませんが、上記に挙げた方法が参考になれば幸いです。


ページは見つかったのか。見つかっていないのか。

Webサービスを利用する際に頻繁に目にすることがある、”404 Not Found”という画面があります。

実際にはエラー表示専用の画面をデザインすることが多いため、メッセージの見栄えはサイトによって異なることが多いです。例えば、弊社コーポレートサイトのエラー画面は、以下のようになっています。

ZYYX 404

このページ内容は、Webサイトのコンテンツにアクセスした際に、URLで指定されたコンテンツが物理的に存在しない場合にWebサーバーが返すものです。コンテンツが存在し、正常にレスポンスされる場合には、”200 OK”が返されます。

このエラーが発生した際、Webサーバーのアクセスログには、HTTPステータス404が発生した旨のログが記録され、エラーページのHTMLにGoogle Analytics等のWebビーコン型のタグを入れておけば、そちら側のログにもHTTPステータス404が記録されます。

さて、HTTPステータスに関しての前置きが長くなってしまいましたが、ここから本題です。

WebCenter Sites(以下、WCS)で動的配信を行う場合、表示されるページは、URLアクセス時に指定されたパラメーターや日付等の検索条件によって変化します。

例えば、コーポレートサイトにニュースを掲載する場合を考えてみましょう。データの運用は、標準的には以下のようになると思います。

  1.  掲載したいニュースの原稿や画像素材を準備する。
  2. ニュースの原稿や素材をWCSのアセットに登録し、公開開始日時を指定する。
  3. WCSの未来日プレビュー機能を使用し、指定した日時にニュースが公開されることを確認する。
  4. ニュースのアセットを配信環境へパブリッシュし、公開開始日時を待つ。

その後、パブリッシュしたニュースの公開開始日時を過ぎると、ビジター画面にニュースが表示され、Google等の検索エンジンのクローラーのアクセスがあり、インターネット上の検索サイトで公開したニュースが検索できるようになります。

さて、ニュース公開後、例えばニュースの公開開始日時を間違って早めに設定してしまった場合のことを考えてみます。公開してしまったニュースを取り下げるには、以下のような方法があると思います。

  •  公開開始日時を修正したアセットをパブリッシュしなおす。
  • ニュースのアセットに非表示にするためのフラグがあれば、それを設定してパブリッシュしなおす。
  • ニュースのアセット自体を削除する。

上記いずれの対象方法を行っても、配信環境では「表示対象のニュースが存在しない」とテンプレートのロジックで判断され、該当のニュースは表示されなくなります。

ここに落とし穴があります。

テンプレートの判断でアセットの有無を判断して出力する結果は、サーバー側の処理としては正常なものですので、見た目上「このページは存在しません」と表示したところで、HTTPのレスポンスとしては”200 OK”が戻ってしまうのです。(この動きを、「ソフト404」と呼びます)

ソフト404は、Webサイトを見ているのが人間だけならば問題はないのですが、実際には、以下の弊害を伴っています。

  • 一度検索エンジンのクローラーによって該当ニュースのページがインデックスされた場合、ソフト404で取り下げを行っても存在しないページの内容や、場合によっては、エラーメッセージを表示したページの内容が検索エンジンのインデックスに残ってしまい、検索の対象となってしまいます。検索にはヒットするのに、実際にリンクをクリックすると内容が無い、ということで、サイトにアクセスしてきたビジターの不信感につながってしまいます。
  • ソフト404を発生させた場合、アクセスログ(Webサーバーのログ、Google Analytics等のWebビーコン型のログのどちらにも)に存在しないページヘのアクセスが”200 OK”として記録されてしまうため、ログをベースにしたコンテンツ改善(リンク切れの解消や、ページアクセス解析)がやりにくくります。

表示させるページが存在しない場合に、ソフト404ではなく、「本当の404 Not Found」を発生させるにはどうすればいいのか。ここで、WCSの仕様が壁となって立ちはだかります。

なんと、WCSでは、標準でHTTPステータスの変更方法がないのです。

WCSのテンプレートはJSPベースですが、setStatus等でHTTPステータスを任意に変更することができません。(同じように、動的にContent-Typeの変更もできないのですが、それは別の機会に)

とはいえ、「いやー、WCSの仕様なので仕方ないんですよねー」で通じるような世の中でもありませんので、弊社はどう解決しているかというと、レスポンスを書き換える「サーブレット・フィルタ」を追加開発して、対処を行っています。

サーブレット・フィルタは、サーブレットを呼び出す前後でリクエストとレスポンスの値に対して処理を行うことができるプログラムです。実際の処理の詳細は本稿では割愛させていただきますが、簡単にいうと、テンプレート側でソフト404のレスポンスを返す際に、サーブレット・フィルタ側でHTTPステータスを”200 OK”から、”404 Not Found”に変更してブラウザやクローラーへ返すようにしています。これで、ソフト404にまつわる弊害も、完全に解決です。

蛇足になるかもしれませんが、このソフト404問題、設計・開発時に意外と見落とされがちでもあるので、本稿が何らかのヒントになることを期待しております。


新年のご挨拶

読者の皆様

新年あけましておめでとうございます。
本年もWebCenter Sites 開発者ブログと株式会社ジークスを、どうぞよろしくお願いいたします。

新年第一回目のブログは、1/17(金)に公開予定です。
どうぞ、お楽しみに。


アセットのデータ取得方法によるパフォーマンス測定(その1)

今、世界はクリスマスムードにつつまれています。クリスマスツリー、クリスマスケーキ、クリスマスプレゼント、クリスマスソング、クリスマスパーティ、クリスマスイベント、クリスマスセールなどなどクリスマスがつくものが楽しさあふれでていますね。

そこで今回は、アセット(フレックスファミリーアセット)のデータを取得するのに、より速い取得の方法はないか調べて、皆様にお届けしたいと思います。christomas_1

たくさんのアセットを参照しているページは、キャッシュがきれている状態でページへアクセスすると、少し重く感じられますよね。少しでも早くページが表示されるように、パフォーマンスを考慮した実装が必須になります。今回は、フレックスアセットのデータの取り方に絞って探ってみようと思います。

今回は、WCSで用意されているタグを使って調べてみます。

データの取得にはassetset:getattributevaluesタグと、assetset:getmultiplevaluesタグが用意されています。この2つのタグの処理時間に違いはあるのでしょうか。2つのタグを使って処理時間を計測し、どちらのタグが速い処理を行っているか測ってみます。

アセット1コのデータを取得しただけでは、わからないので、アセット10コ、50コ、100コで計測してみましょう。

データの取得は、アセットがもつ属性から適当に5つ(string型4つ、asset型1つ)を選んでみます。

では、テスト用のテンプレートをそれぞれ作ります。

1)assetset:getattributevaluesタグ


%><assetset:setasset name="asFlexData" type="TestAsset_C" id='<%= ics.GetVar("aidAssetId") %>' /><%

%><assetset:getattributevalues name="asFlexData" attribute="testA" listvarname="listFlexData" typename="TestAsset_A" /><%
%><ics:if condition='<%= ics.GetList("listFlexData")!=null && ics.GetList("listFlexData").hasData() %>'><ics:then><%
  %><ics:listget listname="listFlexData" fieldname="value" output="o_testA" /><%
%></ics:then></ics:if><%
%><assetset:getattributevalues name="asFlexData" attribute="testB" listvarname="listFlexData" typename="TestAsset_A" /><%
%><ics:if condition='<%= ics.GetList("listFlexData")!=null && ics.GetList("listFlexData").hasData() %>'><ics:then><%
  %><ics:listget listname="listFlexData" fieldname="value" output="o_testB" /><%
%></ics:then></ics:if><%
%><assetset:getattributevalues name="asFlexData" attribute="testC" listvarname="listFlexData" typename="TestAsset_A" /><%
%><ics:if condition='<%= ics.GetList("listFlexData")!=null && ics.GetList("listFlexData").hasData() %>'><ics:then><%
  %><ics:listget listname="listFlexData" fieldname="value" output="o_testC" /><%
%></ics:then></ics:if><%
%><assetset:getattributevalues name="asFlexData" attribute="testD" listvarname="listFlexData" typename="TestAsset_A" /><%
%><ics:if condition='<%= ics.GetList("listFlexData")!=null && ics.GetList("listFlexData").hasData() %>'><ics:then><%
  %><ics:listget listname="listFlexData" fieldname="value" output="o_testD" /><%
%></ics:then></ics:if><%
%><assetset:getattributevalues name="asFlexData" attribute="testE" listvarname="listFlexData" typename="TestAsset_A" /><%
%><ics:if condition='<%= ics.GetList("listFlexData")!=null && ics.GetList("listFlexData").hasData() %>'><ics:then><%
  %><ics:listget listname="listFlexData" fieldname="value" output="o_testE" /><%
%></ics:then></ics:if><%

2)assetset:getmultiplevaluesタグ

%><assetset:setasset name="asFlexData" type="TestAsset_C" id='<%= ics.GetVar("aidAssetId") %>' /><%

%><assetset:getmultiplevalues name="asFlexData" byasset="false" immediateonly="true" prefix="atlst"><%
  %><assetset:sortlistentry attributetypename="TestAsset_A" attributename="testA"/><%
  %><assetset:sortlistentry attributetypename="TestAsset_A" attributename="testB"/><%
  %><assetset:sortlistentry attributetypename="TestAsset_A" attributename="testC"/><%
  %><assetset:sortlistentry attributetypename="TestAsset_A" attributename="testD"/><%
  %><assetset:sortlistentry attributetypename="TestAsset_A" attributename="testE"/><%
%></assetset:getmultiplevalues><%

%><ics:if condition='<%= ics.GetList("atlst:testA")!=null && ics.GetList("atlst:testA").hasData() %>'><ics:then><%
  %><ics:listget listname="atlst:testA" fieldname="value" output="o_testA" /><%
%></ics:then></ics:if><%
%><ics:if condition='<%= ics.GetList("atlst:testB")!=null && ics.GetList("atlst:testB").hasData() %>'><ics:then><%
  %><ics:listget listname="atlst:testB" fieldname="value" output="o_testB" /><%
%></ics:then></ics:if><%
%><ics:if condition='<%= ics.GetList("atlst:testC")!=null && ics.GetList("atlst:testC").hasData() %>'><ics:then><%
  %><ics:listget listname="atlst:testC" fieldname="value" output="o_testC" /><%
%></ics:then></ics:if><%
%><ics:if condition='<%= ics.GetList("atlst:testD")!=null && ics.GetList("atlst:testD").hasData() %>'><ics:then><%
  %><ics:listget listname="atlst:testD" fieldname="value" output="o_testD" /><%
%></ics:then></ics:if><%
%><ics:if condition='<%= ics.GetList("atlst:testE")!=null && ics.GetList("atlst:testE").hasData() %>'><ics:then><%
  %><ics:listget listname="atlst:testE" fieldname="value" output="o_testE" /><%
%></ics:then></ics:if><%

※アセットを格納したリスト作成、ループ処理のコードは省略しています。

測定開始です。

・・・測定・・・測定・・・測定・・・。

出ました。結果です。

アセット数

属性数

タグ

assetset:getattributevalues

assetset:getmultiplevalues

10

5

50.8ms

43.4ms

50

5

438.2ms

256ms

100

5

922.2ms

514.4ms

※動作環境や定義がもつ属性の数によって変動はあります。正確な数値ではありません。

※同じアセットタイプ、同じ定義で、すべて異なるアセットを使っています。

※フレックスアセットの参照はassetset:setassetタグを使っています。

結果を見てみましょう。assetset:getmultiplevaluesタグが速いですね。

assetset:getattributevaluesタグは、アセット数と処理時間を見ると、単純に倍の時間がかかるというわけではなさそうですね。アセット数が多ければ多いほど、時間がかかります。アセットや条件を変更して測っても同じような結果がでました。ということで、アセットタグは、assetset:getmultiplevaluesを使うで決まりですね!メリークリスマスです!

ただ、assetset:getmultiplevaluesタグのデメリットはtext型の属性は取得できないという点です。これは仕様です。ですので、text型の属性は、assetset:getattributevaluesタグを使って取得するしかないですね。うまく使い分けをすれば、十分なパフォーマンスを得ることができると思います。

では、assetAPIを使うとどうでしょうか。タグより速いのでしょうか。

次回(3月頃を予定)はassetAPIを使って測定してみようと思います。christomas_2


【新機能】Vanity URLを試してみる(その1)

今回は、WebCenter Sites 11g (以下WCSと記述)の最新バージョン 11.1.1.8.0 (2013年12月09日時点)から実装された新機能のVanity URLを試してみたいと思います。

1. Vanity URLとは?

まず、Vanity URLとは何か?

Vanity という単語を翻訳してみますと、「虚栄」とか「虚飾」と出てきます。さらに国語辞典で「虚栄」を調べてみると「実質の伴わないうわべだけの栄誉」と出てきました。要するに中身のない見せかけのURLということになります。

WCSのVanity URLとは、ある実際のURLを別の見せかけのURLに変更できる機能となります。通常、WCSで生成されるURLはクエリ文字列を複数組み合わせたものになりますが、それを別の任意のURLに見せかけることができます。その結果、直感的に分かりやすく、また、SEOで優位となるURLを用いることが出来ます。

WCSにはURLアセンブラという同等の機能があります。(11.1.1.8.0でも使えます)その機能との違いについてもおいおい考えてみます。

※以降の検証はJumpstart Kit(以下JSKと記述)の11.1.1.8.0版 のサンプルサイト FirstSiteII を用いて行いました。JSKのインストール方法や使い方については割愛いたします。

※ドキュメントは特に明記しない限りWCSのAdministrator’s Guideを参照しました。

http://docs.oracle.com/cd/E29542_01/doc.1111/e29636/config_vanity_url.htm

2. WebRoots の設定

ドキュメントによると WebRoot は異なった環境でVanity URLがどのように解釈されるかを制御するものとあります。WCSの開発では通常、開発環境、編集環境、配信環境と3つに分かれているケースが多いため、それぞれの環境に対応するためのものかもしれませんが、今回は検証はJSKで行うためあまり気にせず、最初からサンプルとして登録されているものを使用します。

WebRoots_settinig1

Admin UI にログインし、「管理」タブの「WebRoots」以下にFSIIが存在しています。

今回はこれをそのまま使うことにします。

ルートURLにhttp://localhost:9080/cs/fsii が設定されています。

3. Vanity URLの設定

Vanity URLは二つの設定方法により構築することができます。

  • アセットごとの手動設定
  • アセットタイプごとの自動設定(blobも含む)

手動設定では一つ一つのアセットに対して手動でURLを設定します。よってそのアセットを用いて表示しているページのURLを一つ一つ手動で設定する必要があります。

自動設定の場合はアセットのタイプ毎に(もしくは定義ごと)に自動でURLを設定するものとなります。同じアセットタイプのアセットを用いて表示しているページは一つ一つ設定しなくても同じURLパターンとすることができます。この場合は、URLの一部にアセットを識別するための情報を持つ必要があります。

今回はアセットの手動設定方法を試してみます。(自動設定は次回)

4. アセットごとの手動設定

(参考:User’s Guide:http://docs.oracle.com/cd/E29542_01/doc.1111/e29605/user_formmode_createdit.htm#BABJCGIJ

JSKを起動しFirstSite IIのホームを表示すると以下のようなURLとなっています。

manual_setting_0

http://localhost:9080/cs/Satellite?c=Page&childpagename=FirstSiteII%2FFSIILayout&cid=1118867611403&pagename=FSIIWrapper&rendermode=previewnoinsite

クエリを複数組み合わせたURLとなり、長くて分かりづらいです。

今回はこのURLをVanity URLに変更してみます。

今回はアセット毎のVanity URLの設定ということで、アセットにURLを設定するのですが、その際ページの表示に用いるプライマリアセットに設定する必要があります。

先のURLを見ると分かる通りc=Page、cid=1118867611403 となっているため、アセットタイプがPageでidが1118867611403であるものがプライマリアセットとなります。Contributor UI を起動し、サイトツリーから該当のアセットを参照します。

manual_setting_2

URLタブをクリックし、鉛筆アイコンをクリックします。

manual_setting_3

URL 見せかけのURLを設定します。今回は 「home.html 」とします。
ホスト WebRoots で設定した値が入力されているため、そのままとします。※ホストとURLに設定した文字列を組み合わせたURL

http://localhost:9080/cs/fsii/home.html

が実際にアクセスされるVanity URLとなります。

デバイスグループ 今回はこのままとします。
Template 「FSIILayout」を設定します。ホーム現行のURLのchildpagenameに設定されているものとなります。
ラッパー 「FSIIWrapper」とします。ホーム現行のURLのpagenameに設定されているものとなります。

その後、「追加」ボタンをクリックします。

調査画面に戻りURLが追加されていることを確認します。

manual_setting_4

では、このURLでアクセスできることを試してみます。

manual_setting_5

無事にアクセスできました。

また、HTML中のHomeへのリンクもVanity URLに変更されていることを確認します。

manual_setting_6

以上でアセット毎のVaniy URL の手動設定は完了です。非常に簡単です。

次に設定周りを確認しておきます。

5.Vanity URLを使う際の設定

今回のJSKのサンプルサイトの場合はすでに設定されているため特に改めて設定を行う必要はありませんが、Vanity URL自体を使うには、URLリファレンスフィルターの設定が必要となり、また、直接Vanity URLに設定したURLでアクセスできるためには、URLリライトフィルターの設定が必要となります。

まずURLリライトフィルターから見ていきます。

  • URLリライトフィルター

アプリケーションのweb.xmlを参照します。

<filter>
<filter-name>URLRewriteFilter</filter-name>
<filter-class>COM.FutureTense.Servlet.URLRewriteFilter</filter-class>
<init-param>
<param-name>SitePrefix</param-name>
<param-value>fsii,avi</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>URLRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

上記の設定ではコンテキストパス /cs にサイトプレフィックスを加えたパス以下すべてに対してURLリライトが有効となります。

/cs/fsii/*

よって、今回設定した、/cs/fsii/home.html はURLリファレンスフィルターで処理されます。

URLリライトフィルターでは内部的にURLを以下の形式に分割します。

http://localhost:9080/cs/Sites?lookuppage=/cs/fsii/home.html&lookuphost=http://localhost

ここでSites というサーブレットが出来ますが、こちらはURLリファレンスフィルターによって処理されます。

  • URLリファレンスフィルター

こちらもデフォルトで設定がされています。(基本変更する必要はないと思います)

<filter>
<filter-name>WebReferenceFilter</filter-name>
<filter-class>COM.FutureTense.Servlet.WebReferenceFilter</filter-class>
</filter>
<filter-name>WebReferenceFilter</filter-name>
<url-pattern>/Sites/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

Vanity URLはこちらのフィルターによってメインで処理が行われます。

lookuppageとlookuphost のパラメータで渡ってきた値に対応するVanity URLの設定があれば、そのURLに対応するページが表示されます。

このlookuppageとlookpuhost をクエリとして渡すことができるのであれば、administrator’s guide にあるように、Apacheのmod_rewrite でアクセスしても構いません。その場合はURLリライトフィルターを用いずに対応が可能となります。

6.まとめ

簡単にまとめますと、手動設定ではプライマリアセット一つ一つに対して任意のURLを設定できます。その見せかけのURLにアクセスされた場合、URLからプライマリアセットが特定され、同時に設定したテンプレート等の情報からURLが生成され、ページが表示されます。(おそらく内部的にそうなっているのだろうと思います)

また、Vanity URLを設定したURLでアクセスできるページへのリンクが設定されている箇所についても同時にVanity URLに置き換えられます。

設定はURLリファレンスフィルターが必要となり、必要に応じてURLリライトフィルターを用います。

URLアセンブラでも同等のことは実装次第では行うことが可能かも知れません。ただ、URLアセンブラではURLの変換ロジック部分を自分で実装する必要があり、またContributor との連携もおそらくありません。詳細は次回にもう少し考えてみます。

  • 次回予告(3月頃予定)

Vanity URLの自動設定を試してみます。

First SiteII の On Sale Items のリスト内の各詳細ページへのリンクURLは現在URLアセンブラにてURLが設定されています。

例えば、一番上の「Nanomax Flat-Panel LCD HDTV」のURLは以下の通りとなっています。

http://localhost:9080/cs/Satellite/FirstSiteII/FSII/Product_C/1196193243217/1118867611403?packedargs=locale%3D1154551493541

こちらの上書きという形でVanity URLの自動設定を設定してみます。

manual_setting_0

※上記の記述は担当が調査した範囲での記述となります。その正確性、完全性についてはジークス社は責任を負いません。


WebCenter Sites案件に携わって

 これまでに複数のWebCenter Sites案件に携わり、感じたことをお話したいと思います。

 WebCenter Sites(以下 WCS)は、Webサイトのコンテント管理だけを行う単純なCMS(Content Management System)のみならず、エンドユーザーのWeb体験全体を管理できるWeb Experience Management(WEM)としての機能を有する高機能な製品です。
今回は、弊社のこれまでの導入実績として多かった、Webサイトを構築する開発案件についてお話します。(WEMのお話は、また別の機会に。)

 その導入の目的はサイトを閲覧するユーザに対して快適で見やすい環境を提供することと、その運用を行うクライアント様に対して、快適に運用できる環境を提供することです。
どちらか一方でも十分に実現できないということは絶対に避けなければなりません。

 WCSは、さまざまなニーズに応える高いポテンシャルがありますが、それを使いこなすための多くの準備が必要になります。
その作業はクライアント様と、我々開発ベンダーとの緻密な共同作業になります。

 まずは、従来行っていた製品や商品の管理方法、サイトへの出力方法を理解した上で、WCSを導入することで、何が変わるか?何が便利になるか?を十分整理し、説明します。
WCSの案件が他のサイト構築案件に比べ、要件定義に多くの時間を費やすのは、このように従来の業務を整理し、新しい業務体系を提案するところから始まるからです。
時にはクライアント様の職場にお邪魔して実際に1日でどのような業務が発生しているのか、その流れがどのようになっているのか見学させていただくこともありました。

 開発手法としては、従来型のウォーターフォールモデルを基本にしますが、特に製品や商品の登録、管理、出力部分に関しては、プロトタイプを先行して作成し、クライアント様に成果物を確認していただきながら部分的に反復して進めるアジャイル開発風の手法を使うことが効果的だということがわかってきました。
早めに入力のUIや、出力パフォーマンスなどを確認していただき、問題点があれば解決します。
それに合わせて、要件達成のゴールイメージ、スコープを明確にしておくことも重要になります。

development_process

 製品や商品を扱うページの表示ロジックは複雑になりがちです。
多くのデータがリレーションを行い表示されるため、表示速度など出力パフォーマンスは重要な課題となります。
WCSには高性能なキャッシュの仕組みがありますが、基本的な表示ロジックが高速でないと、キャッシュの生成に膨大な時間がかかる結果になり、ユーザビリティの低下に繋がります。
複雑なロジックを伴う部分に関しては、とりあえず作ってみた的な開発の進め方は多くの手戻りが発生するため、 運用方法と、その運用頻度と、閲覧する側のユーザビリティのバランスを考え、適切なパフォーマンスが得られるように設計段階から十分に考慮する必要があります。
パフォーマンス向上のための施策は、このようにそのサイトやコンテンツの運用方法、閲覧側の要求に則したものを実現しなくてはいけません。

 通常の案件に比べ、大規模な構築になるので時間もかかり苦労も多いですが、構築したサイトが様々なところで事例紹介されていたり、クライアント様から「掲載している製品や商品に関する問合せが増えた」「運用が楽になった」「良いサイトが出来上がった」とお言葉をいただいたりすることが、我々開発ベンダーの次への原動力になっています。

 余談ですが、WCSは旧製品名をFatWire Content Serverといいます。
WCS案件に携わる度に何故かFatになっていく自分をなんとかすることが目下の最重要課題です。