2012年7月10日火曜日

UTF-8のJSPで文字化け

まだ理解はしていないけど、すべてエンコーディングはUTF-8に統一しているJSPで、文字化けする場合がある。
|がï½になったりしている。JSP内に直接記述しているマルチバイト文字が文字化けしているだけ。
メイン画面があり、そのメイン画面にincludeディレクティブで、ヘッダやフッタをインクルードしている。
このヘッダやフッタで直接マルチバイト文字を使用している部分やJSP内のコメントが文字化けをしている。
国際化により、ラベルなどはすべてプロパティファイルを使用している。
プロパティファイルに記述している部分は文字化けをしていないので、JSPからJavaにコンパイルするときに文字化けになったと考えられる。
すべてのJSPは(インクルードするファイルも含めて)UTF-8であることは確認している。
また、メイン画面のcontentTypeもインクルードするファイルのpageEncodingもUTF-8にしている。
なぜ一部の画面だけ文字化けするのか不思議だった。
JSPをテキストエディタで開いてもUTF-8だし、この時点では文字化けもしていない。
再度テキストエディタで開いてみると文字化けをしているファイルはUTF-8(BOMなし)で、文字化けをしていないファイルはUTF-8(BOMあり)となっている。
BOMを追加して実行してみると見事に文字化け解消。ただ、すべてのBOMなしで文字化けが出ているかというと、そうではなく、文字化けしていない画面がほとんど。
開発機のTomcat6では問題なし。Weblogic 10や11で発生した。
文字化けをする画面を調査して、すべてBOMありに直さないと。
[雑記]jsp utf-8 文字化け 解消に書いてある。

2012年7月7日土曜日

Meteorで簡単なチャットプログラムを書いてみた

次に、メッセージ送信と送信したメッセージを表示する領域を作成する。
送信領域はログインした時のみ表示する。
メッセージ送信領域はメッセージ入力領域と送信ボタンを配置し、送信ボタンを押すとメッセージがデータベースに登録される。
メッセージ表示領域はデータベースに登録されたメッセージを全て表示する。
まず、messagesというコレクションを宣言する。RDBMSのように、予めテーブルやフィールドを作成する必要はない。
Messages = new Meteor.collection("messages");

送信ボタンを押したときに、そのコレクションにメッセージを登録する。
Template.login.events = {
...
  "click #send": function(event){
    var message = $("#message").val();
    var loginName = Session.get("loginName");
    Messages.insert({userName: loginName, message: message, postTime: new Date().toString()});
  }
}

メッセージコレクションの全てのメッセージを登録時間の降順に取得する。Template.messages.messages = function(){
  var messages = Messages.find({}, {sort: {postTime: -1}});
  return messages;
}

HTMLテンプレート
<head>
  <title>discussion room</title>
</head>
<body>
  <h1>Discussion Room</h1>
  {{>login}}
  {{>messages}}
</body>

<template name="login">
<div>
  <div>
    <label for="loginName">ユーザー</label>
{{#if loggedin}}
    <span>{{loginName}}</span>
{{/if}}
{{#unless loggedin}}
    <input id="loginName" type="text">
{{/unless}}
    <button id="enter">{{enter}}</button>
  </div>
{{#if loggedin}}
  <div>
    <input id="message" type="text">
    <button id="send">送信</button>
  </div>
{{/if}}
</div>
</template>

<template name="messages">
<div>
{{#each messages}}
  <div>{{userName}} {{message}} {{postTime}}</div>
{{/each}}
</div>
</template>


2台のPCでブラウザを立ち上げ、このサイトを呼び出す。
両方に別の名前で入室する。一方のPCでメッセージを送信すると、そのメッセージがリアルタイムにメッセージ表示領域に両方に表示される。

Meteorで簡単なチャットプログラムを書いてみた

Meteorを知ったのは今年始め頃。どんなものか簡単なチャットプログラムを書いてみた。
元来ブラウザはデータをサーバーから取ってくるので、ブラウザでチャットプログラムを書こうと思ったらポーリングをする必要がある。
それだとサーバーに未読のメッセージがないのにクライアントからサーバーに余分な通信が発生する。
また、できるだけリアルタイムにデータを取得するためにはポーリング間隔を短くする必要がある。そうするとさらに余分な通信が発生する。
COMET技術はあるものの標準的な仕様にはなっていない。
HTML5が今後、標準仕様になっていくことは確実の今となっては、リアルタイム性が要求されるWEBプログラムは仕様の一部のWebSocketを使用するのは当たり前な流れだろう。
Meteorは、まさにそのようなプログラムを書くのにうってつけのフレームワークと思う。
ただ、リアルタイム性が要求されるWebシステムってあまりないような気がする。
フルスタックなので、思いつきでプログラムを書くのにはよいが、サーバーやDBMSを代えなくては 非機能要件が満たせない場合の足枷になる場合もある。
しかし開発環境構築からDeployの容易性やプログラムの修正が即座にブラウザに反映されるなど、目からうろこの機能がある。

Node.jsが動作しているので、サーバーサイドもJavascriptで書けるのは技術者の確保をしやすいかも。しかし、Javaなどの言語と比べるとデバッグ環境が少ない。

チャットの簡単なプログラムを書いてみた。
まずはローカル実行環境構築だか、恐ろしいほど簡単。
Linuxに CentOS6.2 で以下のコマンドを打つだけ。
curl install.meteor.com | sh
これで自分のPCにMeteorの実行環境ができた。
それでは、mysterytripというプログラムを作っていこう。
meteor create mysterytrip
これでカレントディレクトリ配下にmysterytripというディレクトリができた。
cd mysterytrip
ここにmysterytripという名前のjavascript、html、cssの計3つのファイルができている。
早速このプログラムを起動してみよう。
meteor
これで実行された。
ブラウザでhttp://localhost:3000/と打つと画面にHello World!が表示される。
ここまではなんてことない。
mysterytrip.htmlを開きHello WorldをMystery Tripに変更して保存するとブラウザをリロードしなくてもMystery Tripと表示されるではないか。
データだけでなくコードもリアルタイムに反映される。素晴らしい。

まずはログイン領域を作成する。ユーザー名入力項目と、入室ボタンを配置し、入室ボタンを押すとチャットルームに入れるようにする。
入室中は、ユーザー名は表示専用項目にして、入室ボタンは退室と表示させる。
入室中に退室ボタンを押すと初期画面に戻る。

まずはjavascriptを書く。
ログインしているかをセッションにloginNameというキーの値があるかどうかで判断させる。

isLogin = function(){
  var loginName = Session.get("loginName");
  return = typeof loginName !== "undefined" && loginName !== null;
}

Template.login.loggedin = function(){
  return isLogin();
}
login領域のloginNameに、ログインしていればセッションにあるloginNameキーの値を設定する。
Template.login.loginName = function(){
  var loginName = Session.get("loginName");
  loginName = isLogin() ? loginName : null;
  return loginName;
}
ログインしているかどうかで入室ボタンの表示を変える。

Template.login.enter = function(){
  var enter = isLogin() ? "退室" : "入室";
  return enter;
}
また入室ボタンを押した時の処理
Template.login.events = {
  "click #enter": function(event){
    var loginName = Session.get("loginName");
    Session.set("loginName", isLogin() ? null : $("#loginName").val());
  }
}
次はhtmlテンプレート
<head>
  <title>mystery trip</title>
</head>
<body>
  <h1>Mystery Trip</h1>
  {{>login}}
</body>

<template name="login">
<div>
  <label for="loginName">ユーザー</label>
{{#if loggedin}}
  <span>{{loginName}}</span>
{{/if}}
{{#unless loggedin}}
  <input id="loginName" type="text">
{{/unless}}
  <button id="enter">{{enter}}</button>
</div>
</template>
templateのloginでloggedinがtrueの時(#if)はスタティックでログイン名を表示する。loggedinがfalseの時(#unless)は入力項目を表示する。
まずは入室と退室ができるところまで作成。

2012年6月24日日曜日

Meteor


MeteorはモダンWebサイトを構築するためのとっても簡単な環境である。
Meteorでは、最高のツールを使っても数週間かかる作業をわずか数時間しかかからないというキャッチフレーズである。
Meteorはclient-sideとserver-sideのJavascript framework。ということは開発言語はJavascriptだけで開発ができるということか。
Meteorのインストールやプロジェクトの作成やデプロイはコマンド一つでできるのでとても簡単である。

ただし、Meteorは2012/06/24現在PREVIEW 0.3.7であるので、仕様や動作はまだ安定していないのかもしれない。

Meteorインストール
$ curl install.meteor.com | /bin/sh

たったこれだけ。

あとはmeteorコマンドを使用する。
プロジェクトを作成するのにはmeteor create myapp
しかも、meteorプロジェクトが作成しているサンプルであるtodosなどのプロジェクトを作成するのには
$ meteor create --example todos
とする。
ローカルで実行するためには、
$ cd myapp
$ meteor
これでブラウザから http://localhost:3000/ とするとアクセス可能。
meteorプロジェクトが用意しているサーバーにデプロイするのには、

$ mateor deploy myapp.meteor.com
これでmyappがまだ登録されていない場合にはパスワードを設定して登録ができる。すでに登録されている場合には、設定したパスワードを入力してパスワードが一致した場合にはデプロイが可能。

誤訳があると思うけど、以下にMeteorの7つの原則を示す。


  • Data on the WireHTMLをネットワークに流さない。データを送信して、クライアントがどのように描画するかを決定する。
  • One LanguageJavascriptでクライアントとサーバーの両側を書く。
  • Database Everywhereクライアントまたはサーバーからデータベースへアクセスするためには同じ透過なAPIを使用する。
  • Latency Compensationクライアント上で、プリフェッチとモデルシミュレーションを使用して、あたかもデータベースへの遅延時間無しで接続しているように見えるようにしている。
  • Full Stack Reactivityすべての層、データベースからテンプレートまで、イベント駆動のインターフェイスが使用可能。
  • Embrace the EcosystemMeteorはオープンソースで、現存のオープンソースのツールやフレームワークを置き換えるというよりは、統合をする。
  • Simplicity Equals Productivity何かがシンプルに見えるようにする最善の方法は、実際には単純なものにすることです。きれいで、古典的な美しいAPIを介してこれを達成する。


2012年6月14日木曜日

ODP.NETへ移行


ハンディターミナルからのデータを.NET Frameworkで受け、Oracleに出力するプログラムがあるが、そのプログラムを新しい環境で動作することを検証している。4年以上前のプログラムで、実行環境もOracle 10g, .NET Framework 2.0で動作しているものを、Oracle 11g, .NET Framework 4.0で動作するかを確認した。
当初、問題無いだろうと思ったが、結構ハマったのでその記録。

ハンディターミナルからWindowsへの通信ができることが確認できたので、ハンディターミナルからデータを出力したところ、OCI-22053の例外が発生した。
調べたところ、Microsoft製のSystem.Data.OracleClientにバグがあり、そのバグは直さないと決めたそうだ。さらに、このモジュールは.NET Framework 4.0では非推奨にであるとのこと。
.NET Frameworkのバージョンを2.0にダウングレードしようと思ったが、このサーバーで4.0を対象にしたプログラムが動いているかもしれず、ダウングレードした時の動作は保証されないと言われ、やむなくダウングレードは諦めた。

今後はOracle製のモジュールを使用するようにとネットに書かれていたので、Oracle製のモジュールのODP.NET4をインストールした。幸い、プログラム自体はどのモジュールを使用するかは初期化ファイルに記述するようになっているので、初期化ファイルのプロバイダの値をSystem.Data.OracleClientからOracle.DataAccess.Clientに変更した。

今度はSELECT文でCONNECT BY PRIORしているテーブルを外部結合(+)した結果のフィールドがNULL以外のものを取得するSQL文であるが、NULLのものも結果セットに選択されているため、そのあとの動作がおかしくなっていた。
SELECT LV, ID
FROM (
SELECT LEVEL AS LV
, T1.ID
FROM T1, T2
WHERE T1.ID = T2.T1_ID (+)
START WITH T1.ID = (SELECT T1_ID FROM T3 WHERE ID = :ID)
CONNECT BY PRIOR T1.PARENT_ID = T1.ID
AND T1.ID != 0
)
WHERE ID IS NOT NULL;
ID IS NOT NULLとしているのに結果セットにはIDがNULLのレコードも含まれていた。

WITH句で取得した結果セットに対してNULL以外のものを取得するSQL文に変更した。
WITH T AS (
SELECT LEVEL AS LV
, T1.ID
FROM T1
LEFT OUTER JOIN T2
ON T2.T1.ID = T1.ID

START WITH T1.ID = (SELECT T1_ID FROM T3 WHERE ID = :ID)
CONNECT BY PRIOR T1.PARENT_ID = T1.ID
AND T1.ID != 0
)
SELECT LV
, ID
FROM T
WHERE ID IS NOT NULL;
プログラムを変更する必要があるため、開発環境を構築してプログラムをビルドした。

今度は通信はできるのだが、データベースの接続ができなくなった。調べてみると、ODP.NETはOracle Clientのtnsnames.oraの値を読んでいないためのようだ。
なので、接続文字列をORCLから(DESCRIPTION=以下の文字列に変更したところ、データベースの接続ができた。

すると、前回引っかかったところはクリアしたのだが、次のSQL文でORA-01008が出た。SQL文中のバインド変数とパラメータが一致しないということである。バインド変数の名前とパラメータの名前を比較したが、一致している。ググったところ、ODP.NETではデフォルトではバインドはパラメータの名前ではなく、パラメータの設定した順番に割り当てられるとのこと。同じパラメータを複数使用しているため、SQL文中のバインド変数とパラメータの個数が一致せずにエラーが発生していた。
バインド変数を名前でバインドさせるためにはOracleCommandのBindByNameプロパティをtrueにすればいいのだが、OracleCommandを使用しておらず、DbCommandを使用している。そのため、BindByNameを使用できない。今更OracleCommandに変更するわけにもいかず困っていたが、[Oracle][.NET]dbFactoryによる汎用的なDBアクセスで、ODP.NET独自のBindByNameプロパティを使うを見てDbCommandにBindByNameプロパティがあれば、trueを設定するようにしたところ、プログラムが正常に動作した。
.NET Frameworkにもリフレクションがあるんだ。
これでやっとテストができる。

2012年5月29日火曜日

SSHでパスワードなしでログインする方法のメモ

SSHでパスワードなしでログインする方法のメモがとてもわかりやすくまとめてあったんで、記録。

Unsupported major.minor version 50.0

J2RE1.4の動作環境で動かすため、JRE System LibraryのExecution environmentをJ2RE 1-4にしたにもかかわらず、java.lang.UnsuppportedClassVersionError Unsupported major.minor version 50.0が発生した。調べてみると、JRE System LibraryのExecution environmentがJ2RE 1-5のjarファイルが紛れ込んでいた。このjarファイルの実行環境をJ2RE 1-4にした。