Posted by & filed under Tomcat.

Tomcat ではエラーページがアプリケーション側で設定されていない場合、デフォルトのエラーページが表示されます。

アプリケーション側でエラーページを設定すればいいのですが、設定もれした場合にこういうページがいやだという場合は、カスタマイズして好きなページにすることができます。なお、下段のサーバーのバージョンを変更したい場合は、プロパティファイルの変更ですみます。これについては、Tomcat7のSecurity Considerationsのページに記述があります。

カスタマイズするには、ErrorPageReportValve を拡張することになります。拡張する場合には、プロジェクトを作成し、拡張したクラスとMBeansを記述したファイルを含めてjarにして、$CATALINA_HOME/lib に追加し、 server.xml を編集します。

まず、追加するクラスですが、org.apache.catalina.valves.ErrorReportValve を継承し、report メソッドをオーバーライドして適当なレスポンスを返すようにします。

package info.hilife_jp.tech.catalina.valves;

import java.io.IOException;
import java.io.Writer;

import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.util.ServerInfo;

public class MyErrorReportValve extends
		org.apache.catalina.valves.ErrorReportValve {

	@Override
	protected void report(Request request, Response response,
			Throwable throwable) {
		// Do nothing on non-HTTP responses
        int statusCode = response.getStatus();

        // Do nothing on a 1xx, 2xx and 3xx status
        // Do nothing if anything has been written already
        if ((statusCode < 400) || (response.getContentWritten() > 0)) {
            return;
        }

        try {
			response.sendRedirect("http://www.yakult-swallows.co.jp/");
		} catch (IOException e) {
		// ignore
		}
	}
}

これは、エラー時に外部のURLにリダイレクトさせる例です。固定のHTMLページを表示させたい場合は、オーバーライドする元のメソッドを参考にHTMLを組み立てることになります。

次に、MBeans記述子です。作成したクラスと同じパッケージにmbeans-descriptors.xml を作成します。

<?xml version="1.0"?>
<mbeans-descriptors>

  <mbean name="ErrorReportValve"
         description="Implementation of a Valve that outputs HTML error pages"
         domain="Catalina"
         group="Valve"
         type="info.hilife_jp.tech.catalina.valves.MyErrorReportValve">

    <attribute name="asyncSupported"
               description="Does this valve support async reporting."
               is="true"
               type="boolean"/>

    <attribute name="className"
               description="Fully qualified class name of the managed object"
               type="java.lang.String"
               writeable="false"/>

    <attribute name="info"
               description="Information about this implementation"
               type="java.lang.String"
               writeable="false"/>

    <attribute name="stateName"
               description="The name of the LifecycleState that this component is currently in"
               type="java.lang.String"
               writeable="false"/>

  </mbean>

</mbeans-descriptors>

これは、元のErrorReportValve と同じパッケージにある、mbeans-descriptors.xml を参考にクラス名のみ書き換えています。
ここまで作成したら、jarにパッケージングして作成したjarを $CATALINA_HOME/lib に入れておきます。

最後に、追加したErrorReportValveを使うように、server.xml を編集します。Host要素にerrorReportValveClass属性を追加します。

<?xml version="1.0" encoding="UTF-8"?>
<Server>
  <Service>
    <Engine>
    <!--中略-->
      <Host appBase="webapps" autoDeploy="true"  name="localhost" unpackWARs="true"
        errorReportValveClass="info.hilife_jp.tech.catalina.valves.MyErrorReportValve">
        <!--中略-->
      </Host>
    </Engine>
  </Service>
</Server>

これでTomcatを起動して、適当にエラーを起こせば(存在しないリソースを指定して404エラーを出すのが簡単でしょう)、指定したページにリダイレクトされるはずです。
また、MBeanが設定されているかどうかは、VisualVMなどを使えばわかります。

Posted by & filed under MetroStyleApps, Silverlight.

今日、Metro style appをテーマにした「第8回 .NET中心会議 Windows 8 時代に向けてアプリ開発と技術選択を考える」に参加してきました。

この中で、Metroの.NETで使えるWinRTはどちらかといえばSilverlightに似ているというお話が出てきました。そして、非同期処理を書くための構文としてasync/awaitが紹介されていました。
そこで、ん?と思ったのが、今までのSilverlightで使うことのできたDispatcher.BeginInvokeとは実行順が違うのではないかということです。下のSilverlight5のコードをご覧ください。

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    Debugger.Log(1, "c", "1");
    Dispatcher.BeginInvoke(Do);
    Debugger.Log(1, "c", "2");
}

private void Do()
{
    Debugger.Log(1, "c", "3");
    var req = WebRequest.Create("http://techblog.hilife-jp.info"); ;
    req.GetResponseAsync();
    Debugger.Log(1, "c", "4");
}

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    Debugger.Log(1, "c", "1");
    DoAsync();
    Debugger.Log(1, "c", "2");
}

private async void DoAsync()
{
    Debugger.Log(1, "c", "3");
    var req = WebRequest.Create("http://techblog.hilife-jp.info"); ;
    await req.GetResponseAsync();
    Debugger.Log(1, "c", "4");
}

async構文は、Async Targeting Pack for Visual Studio 11 Beta を当てると使えます。Nugetから、Microsoft.CompilerServices.AsyncTargetingPackをインストールしてください。
(ちなみにAsync CTP は VS11BetaのSilverlightでうまく認識しない問題があって、こちらがリリースされました。提供は続いていますが、サポートはされないようです)

Button_Click_1とButton_Click_2でデバッガーに表示される文字の順序が違ってきます。
Button_Click_1では、

1234

Button_Click_2では

1324

と表示されます。

これは、SilverlightのDispatcher.BeginInvokeでは、それを呼び出したメソッドを抜けるまでは、いったん非同期処理がQueueに積まれて、メソッドを抜けてから実行される形になっているためです。
このあたりはすでに neucc先生が図つきで解説されているので詳しくはそちらで。

というわけで、MetroのWinRTはSilverlightに近いからはじまりましたが、Metroとは関係なく、SilverlightではDispatcher.BeginInvoke と async/await の動きが少し異なってくるよ、というお話でした。
(asyncの方はあくまで現時点での話ですが)

Posted by & filed under MetroStyleApps, Node.js.

というわけで、発表資料になります。

デモがうまくいかなった理由ですが、恥ずかしながらこんな理由でした。

また、Metro style apps でローカルに接続する場合は下記に注意しないといけません。

今回のようにデモ目的で開発環境を使うのであれば問題ありませんが、通常のアプリとしては使えないということですね。

今回のセッションではなぜか JavaScriptのセッションがほとんどだったのですが、それと同時にJavaScriptでのMetro 開発が現時点では難解だという意見が多かったですね。(JavaScript が難しいというより、WinJSの使い方がわかりづらい、構造が難解だという意味で)
このあたりは、今後の Release Previewで改善されることも期待したいところです。

私自身は次回はC# をテーマに話したいなあと思っているところです。

Posted by & filed under MetroStyleApps, Node.js, 告知.

次の土曜になりますが、MetroStyleDeveloper #03 で発表します。
今回は、最近Node.js をさわっていることもあり、Metro style apps をJavaScriptで作り、サーバーサイドもNode.js 使ってJavaScriptで作ってみることを紹介してみたいと思います。
特に、Node.js で扱いやすいWebSocketがWindows8でサポートされたこともあり、WebSocketを使ったチャットアプリを作ってみます。
サーバーサイドとWeb版のサンプルは、http://techblog.hilife-jp.info:3000/ で公開しています。修正したり、止めたりするかもしれませんが、(うまくいけば)当日はこのチャットと通信できるMetro style apps を紹介します。
ほかにも興味深いセッションがたくさんあります。
すでに満員でキャンセル待ちですが、UST配信する予定ですし、ぜひごらんになってください。

Posted by & filed under nginx.

このブログは、 techblog.hilife-jp.info というアドレスをトップページにしています。そして、apps.hilife-jp.info というアドレスでサンプルアプリを公開しています。これらは、今回のブログ移転で、同じサーバー(VPS)でホストしています。これらのアドレスへのリクエストは、フロントにおいたnginx が裁いて、それぞれのアプリ(WordPress は PHP、サンプリアプリはそのままnginx がWebサーバーの役割をして公開)に捌いています。イメージとしてはこんな感じです。

今後、同じようにアドレスを追加しつつ、そのたびに別のアプリにリクエストを振り分けようと考えています。(今のところ、別のWordPressやGitを検討中)そのための設定を載せておきます。

まず、nginx のインストールですが、今回使用したサーバーがCentOS 6.2 で、CentOS 6 用にビルドされたnginx のパッケージを使ってインストールしました。バージョンは1.0.14です。インストールについては本家のページを参考にしました。

$ cat /etc/issue
CentOS release 6.2 (Final)
$ nginx -V
nginx version: nginx/1.0.14
built by gcc 4.1.2 20080704 (Red Hat 4.1.2-51)
TLS SNI support disabled
configure arguments: --prefix=/etc/nginx/ --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-cc-opt='-O2 -g -m64 -mtune=generic'

そして設定ですが、 /etc/nginx/nginx.conf (デフォルトのディレクトリの下の設定ファイル)はこのようにします。

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    client_max_body_size 20M;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    keepalive_timeout  5;

    gzip  on;
    gzip_disable "MSIE [1-6]\.";

    proxy_set_header  Host               $host;
    proxy_set_header  X-Real-IP          $remote_addr;
    proxy_set_header  X-Forwarded-Host   $host;
    proxy_set_header  X-Forwarded-Server $host;
    proxy_set_header  X-Forwarded-For    $proxy_add_x_forwarded_for;

    upstream backend {
        ip_hash;
        server 127.0.0.1:8500;
    }

    include /etc/nginx/conf.d/*.conf;
}

デフォルトから変更しているのは、client_max_body_size を大きめに指定して、WordPressのプラグインを手動で追加するときなどに備えています。また、IE6以下を除いて、gzip を有効にしています。そして、upstream ディレクティブを指定しています。

アプリごとの設定は、/etc/nginx/conf.d/ 以下において、include を使ってよみこませています。
まず、単純な静的ファイルサーバーとしての設定( apps.hilife-jp.info あて)は次のようになります。

server {
    listen      apps.hilife-jp.info:80;
    server_name  apps.hilife-jp.info;

    location / {
        root   /usr/share/nginx/apps;
        index  index.html index.htm default.html;
    }

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

listen ディレクティブとserver_nameディレクティブで、 apps.hilife-jp.info の80番ポートあてのトラフィックを受け入れるように設定します。そして、静的コンテンツのルートとして、 /usr/share/nginx/apps を指定しています。あとは、このディレクトリに公開するコンテンツを置けばOKです。(nginx を実行するユーザー(今回の設定ならnginx)が読み取れるようにしておいてください)

次に、WordPress用の設定です。

server {
    listen      techblog.hilife-jp.info:80;
    server_name  techblog.hilife-jp.info;

    location /wordpress/wp-admin/ {
        proxy_pass http://backend;
    }

    location / {
            proxy_pass http://backend;
            proxy_redirect http://techblog.hilife-jp.info:8500/ /;
    }
}

server {
        listen       8500;
        server_name  techblog.hilife-jp.info;

        location / {
            root   /var/www/tech;
            index  index.html index.htm index.php;
            if (-f $request_filename) {
                break;
            }
            if (!-e $request_filename) {
                rewrite ^(.+)$  /index.php?q=$1 last;
            }
        }

        location /wordpress {
            root   /var/www/tech;
            index  index.html index.htm index.php;
            if (!-e $request_filename) {
                rewrite ^(.+)$  /wordpress/index.php?q=$1 last;
            }
        }
        error_page  404              /404.html;
        location = /404.html {
            root   /usr/share/nginx/html;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }

        location ~ \.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /var/www/tech$fastcgi_script_name;
            include        fastcgi_params;
        }

        location ~ /\.ht {
            deny  all;
        }
    }

こちらも同様に、techblog.hilife-jp.info 宛のリクエストを受け付けるように設定します。あとの細かい設定は、使用するWordPressのプラグイン(特にCache系)や、ディレクトリの配置の仕方にもよるので、一例になります。

とりあえず、これで動いていますが、パフォーマンスなどに問題が出てきたら、また対応してブログのネタにしていこうと思います。

Posted by & filed under 雑記.

このたび、このブログをBloggerからVPS上のWordPressに移行しました。
トップページのアドレスは同じですが、投稿ごとのURLからhtmlという拡張子がなくなったことと、
昨年分の投稿のURLとコンテンツの移行がまだ終わっていません。

VPSでは、フロントエンドに nginx をおいて、複数のブログやアプリなどのコンテンツごとにリクエストを振り分けようと考えています。この設定ネタもブログに書こうかなと思っています。

Posted by & filed under Silverlight.

土曜日に行いました、Silverlight を囲む会in東京#6 で発表してきました。
今回も神会場と名高いIIJさんの会場をお借りしまして、Smooth Streaming により勉強会の模様のライブ配信+録画をしています。リンクはこちら

さて、私はSilverlight5 の新機能に関するセッションを行いました。
資料はこちらです。

また、説明につかったサンプルアプリは、GitHubで公開しています。
Gitめんどいという方は、発表時点でのサンプルをZIPに固めています

実際のアプリはここで動かしています。(容量の関係でTrickPlayサンプルは除いています)

今後、余裕があればサンプルを追加しようかとも考えていますので、ご要望があればTwitterなどでお知らせください。

Posted by & filed under Silverlight.

今週末に迫りましたが、3/31(土)に Silverlightを囲む会in東京#6 を開催します。今回は(も)、「Silverlight5新機能早めぐり」という題で、Silverlight5 で追加された新機能をざざっと紹介したいと思います。
サンプル画面も作成しているので、当日動かしながら説明できたらいいなぁと思っています。
他にも、豪華講師陣をお招きしていますのでぜひご参加ください。まだまだ参加者募集中です。

そして、Silverlightを囲む会in東京も、 IT 勉強会スタンプラリーに参加します! IT 勉強会スタンプラリーについては、 HPや「日本各地の勉強会に参加してスタンプを集める“IT 勉強会スタンプラリー”が開催 」をご覧下さい。
各勉強会ごとにスタンプを作成していますが、もちろんSilverlightを囲む会も作成しています。Silverlightといえば!、というスタンプになっている(はず)ですので参加される方は楽しみにしていてください。

Posted by & filed under CloudFoundry.

先日、eclipse-integration-cloudfoundry がgithubに公開されました。これは、すでにビルドされたバイナリは公開されていて、Eclipseのpluginとしてインストールして、EclipseからCloudFoundryにJavaアプリを公開したり、アプリの設定を変更したりできるものです。(詳しくはこのページ

プラグインのソースが公開されたことで、クライアント側の処理に自分たちで手を入れることが可能になります。そこで今回は、手始めにソースをcloneしてきて、そのソースを使ってEclipseを起動する手順を紹介してみます。

まずは、プラグイン開発用のEclipseをインストールします。Eclipseプラグイン開発を知っている人はわかると思いますが、デバッグ起動用のEclipseを起動するのは、プラグインを開発するEclipseからになります。 Eclipseのダウンロードページには、たくさんの種類がありますが、今回は、Eclipse classic というものを選びます。classicってなんじゃ?という感じですが、以前のEclipse SDKといわれているもので、Eclipseそのものを開発するために、プラグイン開発用の環境と、Eclipseを構成するpluginプロジェクトが取得できるようになっています。手っ取り早く使うにはこちらがいいでしょう。もしくは、普段Eclipse RCPの開発などでEclipse for RCP and RAP Developers を使っていて慣れているなら、そちらを使って必要なソースを追加してもいいはずです。今回の手順では、Eclipse classic を使います。

ダウンロードして展開して起動したら、まず必要なプラグインをインストールします。GitHubのソースをEclipseから操作して取得したいので、EGit プラグインをインストールします。また、それに加えて、公開されている eclipse-integration-cloudfoundry が依存しているpluginプロジェクトを追加するために、eclipse-integration-cloudfoundry そのものもいったんインストールします。この手順は、先に紹介した、このページに書いてあります。

プラグインをインストールしてEclipseを再起動したら、まずはGitHubからソースを取得します。EclipseのGitRepository パースペクティブを開き、clone git repository ボタンを押します。

開いたら、eclipse-integration-cloudfoundry のgitのURLをコピペします。

後は、Nextを押してWizardを完了させてcloneします。つづいて、cloneしたソースからEclipseにプロジェクトをimportします。リポジトリツリーを展開して、WorkingDirectoryを右クリックして、Import Projectsを選択します。

Import existing projectsを選択して、Nextを押すと、プロジェクトが表示されるのですべて選択してImportします。Importしたら、Plugin Development パースペクティブを開きます。

すると初期設定なら自動でビルドが始まります。ほとんどのビルドは通るはずですが(警告はのぞく)、私の環境ではいくつかエラーが出ました。org.cloudfoundry.ide.eclipse.server.tests プロジェクトが「org.apache.commons.lang」というプラグインを必要としているが見つからないというエラーなのですが、どうも依存関係を指定しているものの使っていないようなので、依存関係を削除します。plugin.xml を開いて、MANIFEST.MFタブの該当行を削除します。するとビルドは通るはずです。

そして、Eclipseをデバッグ起動するための起動構成を作成します。Runの右横にあるボタンから、Run Configuration を開きます。
Eclipse Applicationを右クリックしてNewを選択し、新しい起動構成を作成します。
pluginsタブを開き、「plug-ins selected below only」を選択します。これにより起動するEclipseに含めるプラグインを明示的に指定できます。いったん、Deselect All で全選択解除してから、workspace ないのプラグインを全選択します。そのあと、Add Required Plugins を押して必要なプラグインを自動で選択させます。

そのあと、Validate Plug-ins ボタンを押して、必要な依存関係が満たされているかチェックします。すると、今回1つエラーが出たのですが、ソースを確認したところ特に必要としていなかったのでとりあえず無視することにします。

最後にRunを押すと、Eclipseをデバッグ実行できます。が、おそらくJava開発用のプラグインは入っていないので、Serversビューを開いてCloudFoundryの接続先を設定することくらいしかできないはずです。
あとは必要に応じて、起動するときに指定するプラグインを追加していけば、実際に使うときの環境に近づき、そのうえで必要な修正を行ってデバッグすることができます。

Posted by & filed under Java, Servlet.

今日「JavaのServletでアプリ内のクラスファイルって WEB-INF/classes におきますよね?」と聞かれ、そういえばいつもそうしているけどそれってどこで決まっていたっけ(どの仕様がそれを決めていたっけ)とふと疑問に思ったのでメモとして残しておきます。

調べたところ、Servletの仕様、つまりJSRで定められているようです。Servlet 2.5 の場合は JSR 154、 Servlet 3.0の場合は JSR 315 で決められていました。

ダウンロードできる仕様書の、Servlet2.5 の場合は 9.5節、Servlet3.0の場合は10.5節の「Directory Structure」の節に書かれています。まず、そもそもWEB-INF という名前のディレクトリは特別なディレクトリで、クライアントに対して公開されないことになっています。なので、クラスファイルとか依存するjarファイル、設定ファイルなどをここの下に置くわけです。

で、その中に置くコンテンツの代表的なものとして、

  • /WEB-INF/web.xml にデプロイメント記述子 (deployment descriptor)
  • /WEB-INF/classes にクラスファイル
  • /WEB-INF/lib/*.jar にjarファイル

というものがあげられています。

そして、/WEB-INF/classes のクラスファイルと/WEB-INF/lib/*.jar のjarファイルは、アプリケーションクラスローダーから見えなければいけない (must be able to) と書かれています。
さらにクラスロードの順番として、/WEB-INF/classes 下のクラスファイルが先で、その次に /WEB-INF/lib/*.jar というところまで決められています。

というわけなので、Servlet の仕様に従うAPサーバー、つまりTomcatやJava EE 仕様に従うサーバーなど世にあるほとんどのAPサーバーではこの決まりにのっとってディレクトリを作ればいいわけです。

ちなみに、クライアントから/WEB-INF/ 配下へのリクエストに対して、Servlet 2.5 ではすべてのリクエストに対し SC_NOT_FOUND(404) を返せと書かれていますが、Servlet 3.0 では パッケージングされたjarファイル内の静的リソースは除く (except
for the case where static resources are packaged in JAR files) とあるので注意が必要です。