2009年2月17日火曜日

wicketと格闘 mountポジション (イントラ用語集5)

かなり昔に考えていたイントラ用語集のネタが腐る前に掘り起こすことにします。

http://domain.name/context/servlet/path/info.ext

REST的にリソースを考えた上、本来のURIの不透明性よりもリソースのコンテントを示すほうが有用と
考えたところまででした。

Servlet処理はそれなりに進んでいたものの、画面デザインは自身にセンスを持ち合わせていないこともあり、
悩んでいたのですが、JSPでガリガリやるのは画面表示が確認できないし、タグを駆使してもどうしてもJSPは綺麗
に書けないと思ったので(個人的に好きになれない…)、Next Strutsを探す旅と言う意味も込めてイロイロと探って
みた結果、HTMLをそのまま使えるという(POHPと言うらしい)wicketにたどり着きました。

wicketの感想はまたの機会にするとして、wicketでのURLの扱いはかなりユニークに出来ているみたいでフレームワーク
でありながら、拡張も簡単に出来る工夫がしてあるようです。

今回の目的である、リソースを表すURLには
  • 拡張子がない時はhtmlとして扱う
  • htmlを付けたURLでも同じように扱う
事を目的にBookmarkablePageRequestTargetUrlCodingStrategyを拡張した
MixedParamUrlCodingStrategy クラスをマネして参考に作成してみました。
wicket歴も浅いので、いい加減なところもありますが、ツッコミがありましたらお願いします。

コンストラクタ

public FileExtensionUrlCodingStrategy(String mountPath,
Class bookmarkablePageClass, String pageMapName,
String[] parameterNames, String extension) {
super(mountPath, bookmarkablePageClass, pageMapName);
/* TODO 拡張子からMimeを確認する方法がバラバラ
* Resource#detectContentType参照
* 下記はgeronimo-activationライブラリの拡張したものを流用
*/
if (!FileTypeMap.getDefaultFileTypeMap().exist(extension))
throw new IllegalArgumentException("指定した拡張子はサポートされていません。: " + extension );
this.parameterNames = getDesignedParameterNames(parameterNames);
this.extension = extension;
}


getDesignedParameterNamesメソッド

private String[] getDesignedParameterNames(String[] parameterNames) {
boolean found = false;
for (int i = 0; i < parameterNames.length; i++) {
if ("format".equals(parameterNames[i])) {
found = true;
break;
}
}
return found ? parameterNames : (String[]) ArrayUtils.add(parameterNames, "format");
}


appendParametersメソッド Redirect After Postでリダイレクトされる時のURLの生成
ほとんどMixedParamUrlCodingStrategyのマネ。

@SuppressWarnings("unchecked")
@Override
protected void appendParameters(AppendingStringBuffer url, Map parameters) {
if (!url.endsWith("/"))
url.append("/");

Set parameterNamesToAdd = new HashSet(parameters.keySet());
/* Find index of last specified parameter */
boolean foundParameter = false;
int lastSpecifiedParameter = parameterNames.length;
while (lastSpecifiedParameter != 0 && !foundParameter)
foundParameter = parameters.containsKey(parameterNames[--lastSpecifiedParameter]);

if (foundParameter) {
for (int i = 0; i <= lastSpecifiedParameter; i++) {
String parameterName = parameterNames[i];
if ("format".equals(parameterName))
continue;
if (i != 0 && !url.endsWith("/"))
url.append("/");
final Object param = parameters.get(parameterName);
String value = param instanceof String[] ? ((String[])param)[0] : (String)param;
if (value == null)
value = "";
url.append(urlEncodePathComponent(value));
parameterNamesToAdd.remove(parameterName);
}
/* format未指定の場合のみURLに追加
* defaultはhtmlになるが、URLとして表現しない。
* http://server/context/pathinfo
*/
if (parameters.get("format") != null)
url.append(".").append(parameters.get("format"));

parameterNamesToAdd.remove("format");
}

if (!parameterNamesToAdd.isEmpty()) {
boolean first = true;
final Iterator iterator = parameterNamesToAdd.iterator();
while (iterator.hasNext()) {
url.append(first ? '?' : '&');
String parameterName = (String)iterator.next();
final Object param = parameters.get(parameterName);
String value = param instanceof String[] ? ((String[])param)[0] : (String)param;
url.append(urlEncodeQueryComponent(parameterName)).append("=").
append(urlEncodeQueryComponent(value));
first = false;
}
}
}


Pageクラスに渡すPageParameter作成メソッド

@SuppressWarnings("unchecked")
@Override
protected ValueMap decodeParameters(String urlFragment, Map urlParameters) {
PageParameters params = new PageParameters();
/* Add all url parameters */
params.putAll(urlParameters);
String urlPath = urlFragment;
if (urlPath.startsWith("/"))
urlPath = urlPath.substring(1);

if (urlPath.length() > 0) {
String[] pathParts = extensionSplit(urlPath);
if (pathParts.length > parameterNames.length) {
throw new IllegalArgumentException(
"Too many path parts, please provide sufficient number of path parameter names");
}

for (int i = 0; i < pathParts.length; i++)
params.put(parameterNames[i], urlDecodePathComponent(pathParts[i]));
} else {
/* "/"指定時にはディレクトリインデックス "index.html"をマッピングする。
* TODO 将来的にはweb.xml#welcom file listから取得出来るようにしたい。
*/
params.put(parameterNames[0], "index");
params.put(parameterNames[1], DEFAULT_EXTENSION);
}
return params;
}


内部メソッドのおまけ

private String[] extensionSplit(String urlPath) {
String[] elements = urlPath.split("/");
String target = null;
if (elements[elements.length -1].contains(".")) {
target = elements[elements.length -1];
String filename = StringUtils.substringBeforeLast(elements[elements.length -1], ".");
String ext = StringUtils.substringAfterLast(elements[elements.length -1], ".");
elements = (String[]) ArrayUtils.addAll(elements, new String[]{filename, ext});
} else {
elements = (String[]) ArrayUtils.add(elements, extension);
}
return target != null ? (String[]) ArrayUtils.removeElement(elements, target) : elements;
}

0 件のコメント: