2008-07-19

jQuery リファレンスに素早く移動できる jQuery Menu Firefox Extension

jQuery Menu Firefox Extension
For all those jQuery freaks out there. Clicking multiple times to get to documentation is for suckers - add this handy menu right into the top of your Firefox menu and save valuable seconds!
jQuery のリファレンスページに素早く移動するための Firefox3 専用のアドオンです。

このアドオンをインストールすると、次のように、jQuery というメニューが追加されます。そして、メニューの項目を選択すると、該当するリファレンスページを表示します。



ほかに何か特別な機能はないようです。単なるブックマークです。

ですが、ブックマークから選択するよりは、数クリックほど省略できるかもしれません。また、jQuery のリファレンスページの構成が変わったとき、このアドオンのアップデートで反映されるかもしれません。

と、無理やり想像して役立ちそうなことを並べましたが、実際にインストールして使ってみると、そのブックマークとしての用途だけでもなかなか便利です。

2008-07-18

jQuery Chili 2.2 から行番号を表示できるようになりました

jQuery Chili のレシピを静的ロードする方法
こういう状況のときは、jQuery Chili の動的ロードを無効にして、あらかじめレシピを指定しておくという対策ができます。この対策を説明したサイトが見つからなかったので、ここでお伝えします。
以前に紹介した jQuery Chili ですが、新しく 2.2 にバージョンアップして、行番号を表示できるようになりました。

行番号の表示は class を指定して、いろいろとコントロールできる のですが、次のとおり、オプションを指定すると、class を指定することなく行番号を表示できます。

ChiliBook.lineNumbers = true;

このブログにもさっそく適用してみました。jQuery Chili 2.2 に差し替えて、lineNumbers オプションを有効にしました。過去のエントリも不都合なく行番号が表示できています。

2008-07-17

特定のディレクトリを Jaxer Filter の対象外にする方法

Aptana Jaxer (mod_jaxer) は、ディレクトリ単位で Jaxer Filter を指定しますが、このとき、その子ディレクトリも含めて Jaxer Filter が有効になります。

ですが、WEB サイトの構成によっては、そのディレクトリの一部を Jaxer Filter の対象外にしたいことがあります。例えば、http://example.com/ で Jaxer Filter を有効にするが、http://example.com/help/ は無効にするといったときです。

mod_jaxer は、次のとおり、JaxerPassThrough ディレクティブを使って、ディレクトリ単位で Jaxer Filter を無効にできます。無効というよりは Jaxer を適用せず、スルーするというイメージでしょうか。

<Directory "${ANCHOR}/public/help">
JaxerPassThrough
</Directory>

ですので、Jaxer を使うとき、小さくディレクトリ単位で Jaxer Filter を有効にしていく方法のほかに、大きくディレクトリで Jaxer Filter を有効にし、小さくディレクトリ単位で無効にしていくという方法も選択できます。

どちらの方法を使うかは、そのときの WEB サイトの設計と Jaxer の使い方によって変わってくると思います。

2008-07-09

JaxerとDWRを連携する方法

Jaxerはファイル、DB、ネットワークを手軽に利用する為のAPIが組み込まれていて、個人が小さなWebアプリを作るには適していると思います。しかし業務システムで使うとなるとJavaScript以外の言語と連携する必要性がでてくるので方法がないか調べてみたところ、JavaでいうJNIは見つからず、見つけたのはネットワーク機能を使った方法ぐらいでした。調べ方が悪かったかもしれませんが、とりあえず見つけたネットワーク機能を使った方法を紹介します。

ネットワーク機能を使って連携するには、JaxerにはソケットやHTTPやSMTPによる通信をするAPIが提供されてまして、それを使ってWebサービスを呼び出して結果を受け取るという流れになります。ブラウザ・サーバ間と余り変わりません。例えばHTTPで連携するコードはこんな感じになります。

<script type="text/javascript" runat="server" src="lib/jquery/jquery.js"></script>
<script type="text/javascript" runat="server"><!--
var blog = Jaxer.Web.get("http://postal-search-apis-and-solutions.blogspot.com/");
document.write( jQuery("h1[@class=title]", blog).text() );
//--></script>

Jaxer.Web.getメソッドでこのブログのHTML文書を取得し、ブログのタイトルを取得してdocument.writeで出力しています。Jaxer.WebはXMLHttpRequestを利用し易くしたもので、getやsendメソッドでリクエストを送り、レスポンスを戻り値として受け取ることができます。

ネットワークを介した方法は柔軟な反面、準備など色々と面倒です。Jaxerの公式ドキュメントには相手がJava限定ですがより簡単に連携する方法としてDWRを用いた方法が説明されてます。DWRとはJavaScript-Java連携用フレームワークで、JavaScriptから透過的にJavaを呼び出すことができます。

公式ではDWRをTomcat上に設定して説明していますが、Jettyでも動くのでJettyを使って説明します。前提としてAptana StudioプラグインがインストールされたEclipseが既にあるとします。

1.
まずはじめにJavaプロジェクトを作成します。メニューから「File -> New -> Projest」を選んで、表示されたNew Projectダイアログから「New -> Java Project」を選び、適当なプロジェクト名(ここではjaxer_dwrとしました)で作成してください。




2.
作成したプロジェクトをフォーカスしてAlt+Enterキーを押し、プロパティ画面を開きます。そしてビルドパスにJetty(この説明では6.1.3を使用)の依存モジュールを追加してください。



3.
プロジェクトに以下の構成でディレクトリを作成してください。これはJaxer用DWRサーブレットをJetty上で動作させるためです。
jaxer_dwr
  +-- WEB-INF
    +-- classes
    +-- lib




4.
WEB-INF/libディレクトリ配下に次のJARファイルを配置してください。jaxer-dwr.jarはDWRのJARではなく、Jaxerの公式で配布しているJARファイルです。(commons-loggingはここから取得してください)


5.
WEB-INFディレクトリの直下に以下内容でweb.xmlファイルを作成してください。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <display-name>HellowWorld using Jaxer and DWR</display-name>
    <servlet>
        <servlet-name>jaxer_dwr</servlet-name>
        <servlet-class>org.directwebremoting.jaxer.servlet.DwrJaxerServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jaxer_dwr</servlet-name>
        <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>
</web-app>

servlet-class要素にはjaxer-dwr.jarに含まれるサーブレットを指定しています。url-pattern要素にはDWRを介してJavaを呼び出す際のURLを指定します。実際に指定するURLは「/jaxer_dwr/dwr/new/HelloWorld.js」のように先頭にコンテキストルートをつけることになります。

6.
次はDWRを介してJaxerから呼び出すJavaクラスを作成します。内容はいたって簡単で引数のname値を末尾に連結した文字列を返すpublicメソッドを一つ定義したクラスです。以降でこのgetHelloメソッドをDWRを介して呼び出します。
public class HelloWorld{
public String getHello(String name){
return "Hello, " + name;
}
}




7.
プロジェクトフォルダの直下にhello.htmlの名称でブラウザから呼び出すHTMLファイルを作成してください。
<html>
<head>
    <title>Hello World</title>
    <script type="text/javascript" runat="server" autoload="true"
            src="http://localhost:8080/jaxer_dwr/dwr/engine.js"></script>
    <script type="text/javascript" runat="server" autoload="true"
            src="http://localhost:8080/jaxer_dwr/dwr/new/HelloWorld.js"></script>
    <script type="text/javascript" runat="server-proxy">
        function hello(name){
            return HelloWorld.getHello(name);
        }
    </script>
    <script type="text/javascript">
        window.onload = function(){
            document.getElementById("result").innerHTML = hello('aiueo');
        }
    </script>
</head>
<body>
    <pre id="result"></pre>
</body>
</html>

一つ目のscript要素(4~5行目)でDWRのengine.jsを読み込んでいます。DWRを使う場合は読み込む必要があります。他にもutil.jsがありますが、こちらは必要に応じてになります。
二つ目のscript要素(6~7行目)でHelloWorld.jsを読み込んでいます。これにはHelloWorldクラスのpublicメソッドにアクセスする為のJavaScriptコードが含まれており、10行目で実際に使っています。6行目でscript要素のautoload属性にtrueを指定しているは、ページリクエスト時とクライアントからサーバへコールバック時(上記例ではhello関数読み出し時)の両方でスクリプトファイルを読み込む為です。autoload属性にfalseを指定するとページリクエスト時のみ読み込みます。
三つ目四つ目のscript要素(8~17行目)ではブラウザがページの読み込みを終えたタイミングでhello関数を介してJavaのHelloWorld#getHelloメソッドを呼び出し、その戻り値をdiv要素に出力しています。三つ目のscript要素(8~12行目)はrunat="server-proxy"を指定しているのでサーバ側で実行されます。



8.
Jettyを起動するJavaクラスを作成します。内容はプロジェクトフォルダをWebAppのルートとしてポート8080でJettyサーバを起動しています。詳細は主旨とずれるので割愛します。
import java.io.File;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.webapp.WebAppContext;

public class JettyStarter {
public static void main(String[] args) throws Exception{
Server server = new Server();
server.setSendServerVersion(false);
Connector connector = new SelectChannelConnector();
connector.setPort(8080);
server.addConnector(connector);

WebAppContext context = new WebAppContext();
context.setTempDirectory(new File("tmp"));
context.setLogUrlOnStart(false);
context.setContextPath("/jaxer_dwr");
context.setWar(new File("./").getAbsolutePath());
server.setHandler(context);

server.start();
server.join();
}
}



9.
準備が一通り終わったのでJaxerとJettyを起動します。JaxerはEclipseをAptanaパースペクティブへ切り替えて、ツールバーにあるJaxer toolbarを押下して起動します。



Jettyは作成したJettyStarterクラスを実行して起動します。実行はJettyStarter.javaをエディタで開いた状態で「Run -> Open Run Dialog...」メニューを選び、左のリストからJava Applicationを選択した後にリストの上にある「New launch configuration」ボタンを押下して設定が作成し、「Run」ボタンを押下すると実行できます。



10.
最後にブラウザから「http:localhost:8000/jaxer_dwr/hello.html」を参照し、以下の結果が表示されれば成功です。やっていることはページの読み込みが終わったタイミングで、サーバに定義したhello関数に引数("aiueo")を渡して呼びます。サーバのhelloではDWRを介してJavaのHelloWorld#getHello()メソッドを呼び出して結果("Hello, aiueo")を返しています。最後にhello関数の結果をブラウザに表示しています。




DWRを使うとJavaとの連携が楽になるのでプレゼンテーション層はJaxer、ドメイン層以降はJavaでという形で開発できそうです。JaxerだけだとJavaでいうJSPに全部詰め込む形になるので、一般の開発では受け入れられないですがプレゼンテーション層はJaxerとすれば提案し易くなるのではないでしょうか。

2008-07-08

Jaxer ClientFramework と Callback のレンダリングを無効にする方法

Aptana Jaxer で runat="server" の ServerFramework だけを使って、runat="server-proxy" や ClientFramework を使わないという選択をしたいときがあります。例えば、モバイル機器など JavaScript をサポートしない WEB ブラウザのときは、Jaxer をサーバサイドで使うがクライアントサイドでは使わないといったケースです。

Jaxer は HTML をレスポンスするとき、自動的に script 要素を追加します。その不要な script 要素を取り除きたいですし、場合によっては、その script 要素が不都合を招くことがあります。が、次のとおり、サーバサイドで手続きすれば、その script 要素の追加を無効にできます。

Jaxer は、ClientFramework をロードするために、次の script 要素を追加しますが、

<script src="/jaxer/framework/clientFramework_compressed.js?version=0.9.7.2472"></script>

次のサーバサイドのコードで、その script 要素の追加を無効にできます。

Jaxer.response.setClientFramework();

Jaxer は、サーバサイドの状態をクライアントサイドに受け渡すとき、次の script 要素を追加しますが、

<script>Jaxer.clientData = Jaxer.Serialization.fromJSONString('{\"foo\":\"bar\"}');</script>

次のサーバサイドのコードで、その script 要素の追加を無効にできます。

Jaxer.clientData = null;

Jaxer は、runat="server-proxy" でコールバック関数を定義したとき、次の script 要素を追加しますが、

<script>Jaxer.Callback.pageSignature = -220624558; Jaxer.Callback.pageName = 'localhost:8081/aptana/golazo/database.html'; Jaxer.CALLBACK_URI = '/jaxer-server/callback'; Jaxer.ALERT_CALLBACK_ERRORS = true;</script>
<script>
function foo() {return Jaxer.remote("foo", arguments);}
function fooAsync(callback) {return Jaxer.remote("foo", arguments, callback);}
</script>

次のサーバサイドのコードで、その script 要素の追加を無効にできます。

Jaxer.proxies = null;

Jaxer.clientData と Jaxer.proxies を明示的に指定する必要性はないと捉えると思いますが、JavaScript ファイルをインクルードしたとき、その中で Jaxer.clientData を扱っていたり、Function.proxy="true" としていることもありえます。そのときも含めて、すべての script 要素を無効にできます。

2008-07-06

Jaxer.application を使って、データベースの初期化をスキップする

Jaxer におけるアプリケーション、ページ、セッションのスコープの考え方
Aptana Jaxer は、application、page、session、sessionPage という 4つのコンテナを持ち、そのコンテナごとにデータを保持できます。互いにコンテナは独立していて、データの相互関係はありません。ですので、アプリケーション、ページ、セッションを組み合わせたパターンのスコープがあると考えればよいです。
application や page のコンテナにどのようなデータを格納するのか、その使い方が思い浮かばないので、Jaxer のサンプルでの使い方を調べてみました。

Chat アプリケーション の中で、次のとおり、Jaxer.application を使っていました。Chat アプリケーションのテーブルを作成した後、初期化済みを表す isInitialized を格納し、それ以降、テーブルを作成するコードを実行しないようにしています。

(function() {

if (Jaxer.application.get("isInitialized")) return;

var sql;

sql = "CREATE TABLE IF NOT EXISTS users " +
"( id INTEGER PRIMARY KEY AUTO_INCREMENT" +
", username VARCHAR(255) NOT NULL" +
", password VARCHAR(255) NOT NULL" +
", created DATETIME NOT NULL" +
", last_login DATETIME NOT NULL" +
", UNIQUE (username)" +
")";

Jaxer.DB.execute(sql);

sql = "CREATE TABLE IF NOT EXISTS messages " +
"( id INTEGER PRIMARY KEY AUTO_INCREMENT" +
", room_id INTEGER NOT NULL" +
", sent DATETIME NOT NULL" +
", user_id INTEGER NOT NULL" +
", contents TEXT NOT NULL" +
")";

Jaxer.DB.execute(sql);

Jaxer.application.set("isInitialized", true);

})();

ただ、上のコードは、本来ならセットアップのためのページを別に用意するものだと思いますので、特別な手続きをせず、また余計なデータベースの操作しないという、あくまでもサンプルのためのコードに過ぎません。

Jaxer のサンプルからは、application や page のコンテナの明確な使い方は見出せませんでしたが、Jaxer.application を Firefox の about:config のように使ったらどうだろうというアイディアが思い浮かびました。

初期化というタイミングで、アプリケーションの設定を Jaxer.application に格納して、それ以降は Jaxer.application の値で動作させる。そして、運用者向けの設定ページを用意し、そのページで、Jaxer.application の値を変更するという使い方です。これは、どんなアプリケーションでも型にはまると思いますので、Jaxer アプリケーション向けの about:config ページを作ってみようという気になってきました。

Mark of the Web

IEはインターネットとローカルのHTMLファイルでは実行時のセキュリティレベルが違います、インターネット上のHTMLはインターネットゾーンで実行され、ローカルディスク上のHTMLはローカルイントラネットゾーンで実行されます。各ゾーンはIEの「インターネットオプション」→「セキュリティ」タブから変更することができます。

HTMLページを公開する場合、ユーザがダウンロードしてローカルディスク上で利用することも考慮する必要があります。その時、ローカルイントラネットゾーンで実行しては問題がある場合困ります。例えば安全でない外部ページを読み込むHTMLなどです。任意に読み込んだ外部ページにローカルディスクの情報を取得するコードが埋め込まれていた場合、問題が起こります。

そんな場合の為にIEではMark of the Webコードというのがあります。このコードをHTMLに埋め込むとセキュリティレベルをローカルイントラネットゾーンではなく、インターネットゾーンで実行することができます。ローカルイントラネットは制限が緩いので制限されたインターネットゾーンで実行させることでセキュリティを高めることが狙いのようです。詳細を知りたい方はMSDNを参照してください。

Mark of the Webコードは次のように書きます。

<!-- saved from url=(0017)http://yourdomain -->

コードはHTMLの先頭2048バイト以内に埋め込む必要があり、urlの先頭にある0017はURLの文字数です。http://yourdomain は17文字なので0017としています。urlは適当なものを指定できるようです。試しにhttp://aaaaa.com を指定してみましたが期待した通りに動きました。このURLはIEが持っている信頼済みサイトや制限付きサイトと関わりがあり、http://aaaaa.comを制限付きサイトに追加するとスクリプトが実行されなくなります。

Webページを公開する場合はとりあえず埋め込んでおくとよさそうです。

Jaxer におけるアプリケーション、ページ、セッションのスコープの考え方

Aptana Jaxer は、application、page、session、sessionPage という 4つのコンテナを持ち、そのコンテナごとにデータを保持できます。互いにコンテナは独立していて、データの相互関係はありません。ですので、アプリケーション、ページ、セッションを組み合わせたパターンのスコープがあると考えればよいです。

コンテナにデータを格納するコードは、次のとおりです。上から、アプリケーションごと、ページごと、アプリケーション単位のセッションごと、ページ単位のセッションごとを表しています。

<script type="text/javascript" runat="server">
//<![CDATA[
Jaxer.application.set('foo', 'bar1');
Jaxer.page.set('foo', 'bar2');
Jaxer.session.set('foo', 'bar3');
Jaxer.sessionPage.set('foo', 'bar4');
//]]>
</script>

コンテナからデータを参照するコードは、次のとおりです。コンテナの空間は独立していますので、コンテナをまたいだデータの関係はまったくありません。例えば、ページ単位のセッションでデータが見つからないとき、アプリケーション単位のセッションを参照するといったことはありません。

<script type="text/javascript" runat="server">
//<![CDATA[
Jaxer.application.get('foo');
Jaxer.page.get('foo');
Jaxer.session.get('foo');
Jaxer.sessionPage.get('foo');
//]]>
</script>

アプリケーションとページの単位は、configRoutes.js を使って定義できます。次のコードでは、Jaxer.Config.routes を使って、corgi アプリケーションと poodle アプリケーションを定義しています。

Jaxer.Config.routes = [
// corgi
function(uri) {
return uri.pathParts[0] == 'corgi'
? [uri.pathParts[0], uri.hostAndPort + uri.pathAndFile]
: null;
},
// poodle
function(uri) {
return uri.pathParts[0] == 'poodle'
? [uri.pathParts[0], uri.hostAndPort + uri.pathAndFile]
: null;
}
].concat(Jaxer.Config.routes);

Jaxer.Config.routes は配列で、その要素に Jaxer.Util.Url.parsedUrl が引数の Function を指定します。この Function は Jaxer がリクエストを受けたとき、配列の先頭から順番に実行され、その結果によって、アプリケーションとページの単位が決定します。

上の Jaxer.Config.routes は、http://[hostAndPort]/corgi/ 以下を corgi アプリケーション、http://[hostAndPort]/poodle/ 以下を poodle アプリケーションとして定義しています。それ以外のときは、Jaxer の初期値の定義を使うように、元の Jaxer.Config.routes を concat で結合しています。

Function は、引数の Jaxer.Util.Url.parsedUrl を使って、リクエストの内容を確認し、期待するリクエストのときは、2つの要素を持つ配列を返却します。1つ目の要素はアプリケーションの識別子です。Jaxer はこの識別子を使って、アプリケーションの単位を切り替えます。2つ目の要素はページの識別子です。Jaxer はこの識別子を使って、ページの単位を切り替えます。

アプリケーションとページの識別子は、文字列であれば何でもよいです(そうみえる)。アプリケーションの単位は決めることがあっても、ページの単位を決めることは、特別なときを除いてあまりないと思われます。ですので、ページの識別子は uri.hostAndPort + uri.pathAndFile といったように、ページの URL をそのまま使えば間違いはありません。

アプリケーションとページの識別子は、次のように key プロパティで確認できます。

<script type="text/javascript" runat="server">
//<![CDATA[
Jaxer.application.key;
Jaxer.page.key;
Jaxer.session.key;
Jaxer.sessionPage.key;
//]]>
</script>

また、コンテナのデータは、次のとおり、removeAll メソッドですべて削除できます。

<script type="text/javascript" runat="server">
//<![CDATA[
Jaxer.application.removeAll();
Jaxer.page.removeAll();
Jaxer.session.removeAll();
Jaxer.sessionPage.removeAll();
//]]>
</script>

私が調べた限りでは、Aptana Jaxer 0.9.7.2472 の時点では、セッションのタイムアウトをサポートしていないようです。レスポンスの Set-Cookie ヘッダをみると有効期限の指定がありませんので、ブラウザのセッション(ブラウザを閉じるまで)が有効期限になります。

また、removeAll メソッドを明示的に使わない限り、不要になったセッションのデータは、ローカルデータベースに残ったまま削除されません。データベースにはデータの格納日時と更新日時が含まれるので、削除しようと思えばできないことはありません。有効期限という考え方がない以上、削除する必要もないんですけどね。

データベースに残り続けて、データが蓄積していく状況は好ましくないと考えるので、本当にサポートしていないのか、もしくは何か手段があるのかは、今後の課題として調べていきます。

2008-07-02

Jaxer 0.9.7 はレスポンスでリダイレクトできないので代替策をいろいろ試してみた

Aptana Jaxer 0.9.7.2472 は、HTTP レスポンスを使ったリダイレクトの仕組みを提供していません。Jaxer は単なるサーバではなく、Apache2 など WEB サーバのレスポンスフィルタとして機能する仕組みなので、もとのレスポンスを無効化して、リダイレクトするという考え方がフィットしないんだろうと推測します。

といっても、リダイレクトしたいという要望はいくつか上がっているようです。次の Issue は、現在のバージョンだとリダイレクトできないので、ステータスコードと URL を指定してリダイレクトできるようになればよいという趣旨のものです。間違ってたらごめんなさい。

[#JXR-173] add Jaxer.response.redirect(301,url);
Currently there is no support(that i can find or that john knows about) in the framework for redirecting a page using the http header Location: {url} , and setting the resonse code to one of the 3xx values.
次の Issue は、レスポンスに Location ヘッダを入れたらどうだろうという趣旨のものです。間違ってたらごめんなさい。

[#JXR-182] Support jaxer redirect in mod_jaxer
Done. Just need to set Location to the redirect URL in framework when needed.
実際に手元の環境で、次のとおり、サーバサイドで HTTP レスポンスに Location ヘッダを追加してみました。確かに Location ヘッダは追加されるのですが、WEB ブラウザは Location ヘッダを認識せず、リダイレクトしてくれませんでした。

<script type="text/javascript" runat="server">
//<![CDATA[
Jaxer.response.addHeader("Location", "http://www.google.com/", true);
//]]>
</script>

HTTP レスポンスを使ったリダイレクトは実現できないため、クライアントサイドの仕組みを使ってリダイレクトする方法を2通り試してみました。

1つ目は、JavaScript を使ってリダイレクトする方法です。実際はリダイレクトじゃなくページ遷移です。Jaxer.clientData を使って、リダイレクト先の URL をサーバサイドからクライアントサイドに引き渡し ます。クライアントサイドは、Location オブジェクトにリダイレクト先の URL を指定してページ遷移させます。


<script type="text/javascript" runat="server">
//<![CDATA[
Jaxer.clientData.redirectUrl = 'http://www.google.com/';
//]]>
</script>

<script type="text/javascript">
//<![CDATA[
if (Jaxer.clientData.redirectUrl)
window.location = Jaxer.clientData.redirectUrl;
//]]>
</script>

2つ目は、ページをリロードするための meta 要素を追加する方法です。サーバサイドで meta 要素を生成して、head 要素に追加しています。このとき、ページのリロードを0秒後、リロード先の URL を指定し、リダイレクトに相当する振る舞いを促しています。

<script type="text/javascript" src="jquery.js" runat="server"></script>
<script type="text/javascript" runat="server">
//<![CDATA[
jQuery(function($) {
var meta = $('<meta />')
.attr('http-equiv', 'Refresh')
.attr('content', '0; URL=http://www.google.com/');
$('head', document)
.prepend(meta);
});
//]]>
</script>

なお、今後公開される Jaxer 1.0 (RC1) では、Jaxer.Response.redirect(url) メソッドを追加してリダイレクトをサポートするとのことです。なので、次のように、サーバサイドからリダイレクトを指示できるようになるということですね。

<script type="text/javascript" runat="server">
//<![CDATA[
Jaxer.response.redirect("http://www.google.com/");
//]]>
</script>

2008-07-01

Jaxer のエラーハンドリング runat="server-proxy" 編

Jaxer のエラーハンドリング runat="server" 編
サーバサイドでエラーが起きたとき、Aptana Jaxer がどのように振る舞うか調べてみましたので、その結果をお伝えします。Jaxer でサーバサイドのコードを実行するタイミングには runat="server" と runat="server-proxy" がありますが、今回は runat="server" を対象とします。
今回は runat="server-proxy" でエラーが起きたとき、Aptana Jaxer がどのように振る舞うか調べてみました。

runat="server" と同じように runat="server-proxy" で、サーバサイドで例外をスローするコードを用意しました。

<script type="text/javascript" runat="server-proxy">
//<![CDATA[
function foo1(value) {
throw 'ERROR!;'
}
//]]>
</script>

そして、次のとおり、クライアントサイドからサーバサイドを実行してみたところ、次のような Alert が表示されました。

<script type="text/javascript">
//<![CDATA[
foo1('bar1');
//]]>
</script>



クライアントサイドからサーバサイドを非同期で実行しても、同じように Alert が表示されました。

<script type="text/javascript">
//<![CDATA[
foo1Async(function(v) {}, 'bar1');
//]]>
</script>

Alert のメッセージは、エラーの具体的な内容を表すので、そのまま見せると不都合なことがあります。そのときは、Config.DISPLAY_ERRORS = false; とすることで、メッセージを固定にして、エラーの内容を伏せることができます。その固定のメッセージは Config.CALLBACK_ERROR_MESSAGE もしくは Config.CALLBACK_FATAL_ERROR_MESSAGE で指定できます。

Alert を表示しないで、ソースコードでエラーを判別する選択もできます。そのときは、Config.ALERT_CALLBACK_ERRORS = false; とします。

クライアントサイドからサーバサイドを同期で実行するときは、例外をキャッチすることで、サーバサイドのエラーをハンドリングできます。

<script type="text/javascript">
//<![CDATA[
try {
foo1('bar1');
} catch (e) {
console.log(e);
}
//]]>
</script>

クライアントサイドからサーバサイドを非同期で実行するときは、エラーのコールバック関数を指定することで、サーバサイドのエラーをハンドリングできます。成功とエラーのコールバック関数を配列で指定します。

<script type="text/javascript">
//<![CDATA[
foo1Async([
function(v) { console.log(v); }, // 成功
function(e) { console.log(e); } // エラー
], 'bar1');
//]]>
</script>

また、成功と失敗のコールバック関数は、オブジェクトとしても指定できます。

<script type="text/javascript">
//<![CDATA[
foo1Async({
callback: function(v) { console.log(v); }, // 成功
errorHandler: function(e) { console.log(e); } // エラー
}, 'bar1');
//]]>
</script>