2013年2月27日水曜日

jQueryMobile の popup で iframe を表示した際に自動的に閉じる方法

かなり特殊な構成になりますが、jQueryMobile の popup パネルを表示し、そこに iframe で別ページを表示するサイトを作成したときに、少し苦労したところがあるので、記録しておきます。

その popup をあるイベントをトリガーに自動的に閉じる動作をさせたかったのですが、jQueryMobile のマニュアルを参照しながら、以下のコーディングをしました。


  $("#popupPanel").popup("close");

しかしこれでは、うまく動作する端末としない端末がありました(このときは Android では動作したのですが、iPhone では動作しませんでした)。この popup("close") ですが、ある条件のときには、どうやら history.back() の動作をしているようなのです。もちろん通常の popup であれば、history.back() はパネルを閉じるのと同じ効果が出ます。しかし今回作成したのは、内部に iframe で別ページを表示した popup パネルであり、その内部でいくつか遷移が行われている状態です。このため、いくら popup("close") を呼び出しても、iframe 内の履歴が戻るだけで、いっこうにパネルが閉じないのです。

いろいろ調べた結果、以下のおまじないを記述することで、閉じる動作をすることができました。



  $("#popupPanel").popup({history: false});
  $("#popupPanel").popup("close");


jQueryMobile を使いこなすのも、なかなか難しいですね。。。

2013年2月25日月曜日

スマホサイトのiframeの高さ問題

先日OperaがレンダリングエンジンとしてWebkitを採用するというニュースが話題になりました。競争による技術革新が阻害される可能性も指摘されているようですが。。。SEの立場から正直な感想を言わせてもらうと、複数ブラウザの検証とギャップを埋めるコーディングほど無駄な作業はないなーと思うので、レンダリングエンジンが統一されて開発者の負担が軽減されるのであれば、大歓迎です。とは言え、ブラウザによる微妙な違いは、まだ当分悩むことになりそうですが。。。

先日、スマホサイトのiframeの高さがうまく調整できないという問題に直面しました。

<iframe width="100%" ・・・>

として、高さを指定せずに iframe を表示した場合、うまく内部のコンテンツの高さにあわせて高さを調整してくれる端末と、そうでない端末がありました。調べてみると、

iPhone ・・・ OK
Android 2.* ・・・ OK
Android 4.0 ・・・ NG

という結果でした。Android の iframe にはバグも多いという記事も見たことがありますが、こんなものなのでしょうか。iframe 内のコンテンツの高さを計算して高さをそろえる手法もあるようでしたが、内部が別ドメインであるなど、ややこしい問題もあったので、このときはそれらしい高さを iframe に指定することでしのぎました。

だれか、もっと良い解決方法があれば、教えて欲しいです。


2013年2月24日日曜日

Oracle で暗号化

以前、Oracle 上で暗号化をしなければいけない案件があり、ちょっと苦労したのでメモ。トライしたのは、SHA1のハッシュ化です。

まず、DBMS_CRYPTOパッケージの HASH 関数を利用するために、Oracle アカウントに権限を設定します。

grant execute on DBMS_CRYPTO to MYACCOUNT;

DBMS_CRYPTOパッケージについては、PL/SQL から利用するサンプルはたくさん見つかったのですが、SQL のみで実行するサンプルがなかなか見つかりませんでした。試しに、以下の SQL で実行してみました。

select
UTL_RAW.CAST_TO_VARCHAR2(
  UTL_ENCODE.BASE64_ENCODE(
    DBMS_CRYPTO.HASH(UTL_I18N.STRING_TO_RAW('mypassword', 'JA16SJIS'), DBMS_CRYPTO.HASH_SH1)
  )
)
from dual;

そうすると、以下のエラーが出てうまくいきません。

「ORA-06553: PLS-221: 'HASH_SH1'がプロシージャではないか、または未定義です」

それは確かにそうなんですが。。。いろいろ調べた結果、DBMS_CRYPTO.HASH_SH1の値は3であることが判明しましたので、

select
UTL_RAW.CAST_TO_VARCHAR2(
  UTL_ENCODE.BASE64_ENCODE(
    DBMS_CRYPTO.HASH(UTL_I18N.STRING_TO_RAW('mypassword', 'JA16SJIS'), 3)
  )
)
from dual;

これでうまくいきました。ちなみに、

HASH_MD4 ・・・ 1
HASH_MD5 ・・・ 2
HASH_SH1 ・・・ 3

のようです。

Oracle パスワードが期限切れで接続できない

半年ほど前に導入したシステムから、以下のエラーが発生しました。

ORA-28001:パスワードが期限切れです

調べてみると、なんと Oracle は 11g になってからデフォルトで 180日になっているじゃないですか!「ORA-28001」で検索すればいくらでも情報はあるのですが、「Oracle導入時の注意点」ではちっとも見かけなかった情報でした。。。こういった勝手な仕様変更に腹が立つのは私だけでしょうか?

以下の方法で復旧しました。

1. DBサーバ上で sqlplus sys/<管理者パスワード> as sysdba で sqlplus を起動
2. 以下のコマンドを実行して該当アカウントを復旧
  SQL> alter profile default limit password_life_time unlimited;
  SQL> alter user <ユーザID> identified by <パスワード>;
  SQL> alter user <ユーザID> account unlock;

ちなみにトラブルとなったのは JBoss サーバでしたが、この対処のみで自然と復旧することができました。また、トラブルとなったユーザ以外にも、有効期限が切れている(または切れそうな)ユーザを、以下の SQL で調査しました。

SQL> select USERNAME, ACCOUNT_STATUS, LOCK_DATE, EXPIRY_DATE, PROFILE from dba_users;

ここで、必要なアカウントについて、ACCOUNT_STATUS が OPEN 以外(EXPIRED や EXPIRED & LOCKED 等)だと、アカウントが無効(もしくは無効になりかけ)ですし、EXPIRY_DATE が設定されていたら、将来無効になってしまいます。同様に、以下の対処で正常な状態にすることができます。

  SQL> alter user <ユーザID> identified by <パスワード>;
  SQL> alter user <ユーザID> account unlock;

ちなみにこの対処だと、今後の運用ではパスワード無期限が前提になりますが、そもそもデータベースサーバはそれでまったく問題はないと思います。むしろ有効期限切れにする意味が分からない。。。と思うのは私だけ?

Smartyでサニタイジング

PHPのサイトでテンプレートエンジンとしてSmartyを利用している場合、クロスサイトスクリプティング対策としてのサニタイジングのために、HTMLコードのエスケープ処理を行います。エスケープ処理の方法としては、以下のサイトがよくまとまっていると思います。

http://k-holy.hatenablog.com/entry/php-advent-2012

私も試しに

$smarty->escape_html = true;

の設定をしてみたのですが、なぜかエスケープされる場合とされない場合があります。おかしいな~と思っていろいろ試していると、

{$this->name}<br/>

の箇所で

{$this->name} <br/>

のように、{$・・・}の後にスペースを1つ入れるとちゃんとエスケープするようになりました。いったいどんな仕様なのやら。。。


2013年2月23日土曜日

Format系オブジェクトのマルチスレッド問題

運用中のJavaのシステムが、ある日突然おかしな日付(1970年とか)を出力する、そんなトラブルに遭遇した場合は、DateFormat オブジェクトの使い方をチェックしたほうが良いかもしれません。


class DateUtil {
    static SimpleDateFormat myFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    public static String format(Date date) {
        return myFormat.format(date);
    }
}

うっかりこんなコードを書いてしまう人はかなり多いと思います。しかし、Format系のクラスはマルチスレッド対応していないため、Web系のユーザが多いシステムで利用された場合、ごくまれに誤動作を起こし、おかしな出力になったりします。しかもこの現象は、そこそこユーザが多いシステムでも年に数回だったりするので、やっかいです。この場合、Format系のクラスは毎回 new で生成してやる必要があります。


class DateUtil {
    public static String format(Date date) {
        SimpleDateFormat myFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        return myFormat.format(date);
    }
}

ある意味 Java の世界では有名な JDK の不具合(?)ですが、知らない人も結構多いですね。。。