JSF試行錯誤

今更seasar2からJSFへの移行しますよと

思ったこと。

  1. セッションスコープでデータを持ちたいときに@SessionScopeをつけたbeanをInjectするとエラーになる
    1. これで解決
  2. glassfishの変なバグ:トランザクション境界を越えてExceptionを投げられると、どんなエラーでもRollbackExceptionが上位にわたってくる。
    1. 馬鹿でしょ。@Versionでのロック判別できない
    2. これでググろう!GLASSFISH-21172
  3. Javascriptをあまり書かなくて済むから楽。Ext JSではどんなに苦労したことか・・・
    1. dataExporterとかマジ便利
  4. JPAはバカ
    1. LEFT JOINすると基本は別々にSQL実行される。毎回QueryHints.LEFT_FETCHとかsetHintsしないと行けない。
    2. NamedQueryで済むなら定義しとけばいいけど。条件文の個数とか動的に変えたいでしょ普通は
    3. デフォルトがキャッシュが効く設定。shared-cache-modeのデフォルトはキャッシュなしでいいだろ
  5. Faceletを使ったRESTなURLのページが作りづらい
    1. だいたいxxxx.xhtmlを呼び出すものだからね

PythonでPACSにdicomファイルを送る方法

CONQUEST DICOM Serverをインストールしているサーバのfsckが失敗してlost+foundディレクトリに
大量のdicomファイルがあったので、それをCONQUEST DICOM Serverに再登録するためのスクリプト

easy_installでpydicom,pynetdicomをインストールすること

#!/usr/bin/env python
import sys
from netdicom.applicationentity import AE
from netdicom.SOPclass import *
from dicom.UID import *
import dicom
import os
import traceback

remote_host = "xxx.xxx.xxx.xxx"
remote_port = 5678

def OnAssociateResponse(association):
	print "Association response received"

MyAE = AE("自分のAEタイトル", 9999, [	StorageSOPClass,
				VerificationSOPClass],[], [ImplicitVRLittleEndian])
MyAE.OnAssociateResponse = OnAssociateResponse

RemoteAE = dict(Address=remote_host, Port=remote_port, AET="CONQUEST")

print "Request association"
assoc = MyAE.RequestAssociation(RemoteAE)

print "DICOM Echo ... ",
st = assoc.VerificationSOPClass.SCU(1)
print 'done with status "%s"' % st

for dpath, dnames, fnames in os.walk("lost+foundにあったdicomファイル置き場のパス"):
	for fname in fnames:
		full = dpath + "/" + fname
		print full
		d = dicom.read_file(full, None, False, True)
		print "DICOM StoreSCU ... ",
		try:
			st = assoc.SCU(d, 1)
			print 'done with status "%s"' % st
		except Exception as inst:
			print "problem -> ", inst
			print "trace -> ", traceback.format_exc()
print "Release association"
assoc.Release(0)

MyAE.Quit()

あー、PACSぐらい商用のもの導入してほしい。。。

ドキュメント管理ソフトを探す

会社の先輩のお姉さんにドキュメント管理ソフトが欲しい。と言われて、すっかり忘れて1ヶ月ほど放置してたけど
昨日、今日といろいろ探していた。で、試したのは順番に以下の3つ。

  1. OpenKM
  2. NemakiWare
  3. alfresco

今回はCentOS6.3にインストールすることにした。

OpenKM
これは、最新版が6.4だったかな?日本語にローカライズされていない。
5.1はローカライズするSQLファイルがここでダウンロードできた。
http://wiki.openkm.com/index.php/Language_Packs

これは、インストール自体は簡単。ここにあるファイルをダウンロードして実行するだけ
http://www.openkm.com/en/download-english.html

だけど、実際動かしてから基本機能を使えるようにするまでに至らなかった。
文書のプレビューを表示するのにLibreOfficeをデーモンで立ち上げるようにするとかの設定とか
何やらがうまく行かず面倒でやめた。UbuntuでOffice文書をPDF化するJODConverterのWebサービス環境を
作った時は、うまく行ったんだけどなー。
半日悩んだので諦めた。PDF文書をOCR読み込みできる機能があるみたい。OpenKMが一番よさげだったんだけどなー

NemakiWare
国産ソフトなんだそうで。ドキュメントが揃ってそうで期待していたんだが。。。
インストールで躓いた。

Select target path [/root] 
/var/nemakiware
Tomcat configuration
Tomcat port [8080] 
Tomcat port(shutdown) [8005] 
Tomcat port(AJP) [8009] 
press 1 to continue, 2 to quit, 3 to redisplay
Repository configuration 
Repository ID(main) [bedroom] 
Repository ID(archive) [archive] 
press 1 to continue, 2 to quit, 3 to redisplay
CouchDB configuration 
CouchDB host [127.0.0.1] 
CouchDB port [5984] 
press 1 to continue, 2 to quit, 3 to redisplay
Rails configuration 
Choose your Ruby bin path [C:\RailsInstaller\Ruby1.9.3\bin] 
/usr/bin
press 1 to continue, 2 to quit, 3 to redisplay
[ Starting to unpack ]
[ Processing package:  (1/9) ]
[ Processing package: Tomcat (2/9) ]
[ Processing package: NemakiWare CMIS サーバ (3/9) ]
[ Processing package: CouchDB初期データのインポート  (4/9) ]
[ Processing package: Solr (5/9) ]
[ Processing package: NemakiShare CMIS クライアント (6/9) ]
[ Processing package: NemakiShareの初期設定  (8/9) ]
[ Processing package:  (9/9) ]
[ ERROR: The installation was not completed ]
[ Unpacking finished ]

何が原因のエラーか全くわからんし。もっと詳細なエラーログだしてくれー
対処しようがない。
事前にRubyもインストールしないといけなくて、やたら面倒だったし。
1時間で諦めた。

alfresco
ここでダウンロードした
http://sourceforge.net/projects/alfresco/

こんな感じでインストール。事前に準備するソフトもなし。(多分)

chmod a+x alfresco-community-4.2.f-installer-linux-x64.bin
./alfresco-community-4.2.f-installer-linux-x64.bin

対話形式でやっていって、何の問題もなくインストールできた。
以下のURLでアクセス
http://host/share

使ってみたが、フリーのソフトにしてはよくできてるな。と思いました。
EXCELとかPDFとかアップロードして、文書中の文言の内容で検索とかできちゃうし。(あたりまえ?)
しかもSharepointプロトコルを喋ってOffice連携できるのはすごい!!

ただ、メニューをログインユーザのグループとかで切り替えできない。
どんな権限、グループでもサイト(ポータル画面みたいなやつ)を作れてしまう。
まあどうせ、リポジトリしか使わないだろうから無視して使ってもらえばいいんだけど。

Ext JS4.2のform部品にapplyToがない件

今までは院内Webシステムを作るときは、見た目の良さからExt JS3.4を利用していたが、
新しいシステムを作るにあたって新しいバージョンの4.2を使ってみることにした。

Webシステムは、SeasarプロジェクトのSAStrutsS2Daoを使っている。これすごい便利。
Javaで作るなら、これがないともう無理。私アンチPHPですし。自分だけでやるならこれ一択

まあそれはいいとして、Ext JS3.4のときはJSPに書いたhtml:textとかhtml:selectとかのタグに、
こんな感じでExt JSのスタイルを当てていた。

new Ext.form.DatePicker({
    applyTo: "inputタグのid"
});

Ext JS4使ってみると、applyToのコンフィグがない!!

コンボボックスだけはtransformのコンフィグでできたけど、日付選択とか、
ただのテキストボックスとかをExt JSスタイルにする方法が見当たらなかった。

うーん、サンプルみたいにExt JSだけでフォームの入力部品を作っていくと
アクションフォームの値をフォームに反映するのがひじょーに面倒だ。
JSPファイルの中でExt JSのフォームを作るjavascriptを書けばいいんだろうけど、それはしたくない。
他の人はどうしているんだろか?と思ってググってみたが、日本語の情報は皆無だった。

Ext JSって、そもそもどういう使い方を想定しているのか全然わからん。
PHPファイルとかJSPファイルにはinputタグを書かないのがセオリーなの?グリッドはすごい便利なのにその辺が優しくないなー

まあ、外国には同じことで困っている人がいたようで。
これを参考してみた。
http://www.sencha.com/forum/showthread.php?158065-add-applyTo-to-Ext-4.1&langid=14

/**
 * An abstract base class which provides shared methods for Components across the Sencha product line.
 *
 * Please refer to sub class's documentation.
 * @private
 */
Ext.define('Ext.AbstractComponent', {

    /* Begin Definitions */
               
// --------- 省略 ------------------
        me.loader = me.getLoader();
        
        // ここから
        if(me.applyTo) {
        	me.name = me.applyTo;
        	var elem = Ext.get(me.applyTo);
        	me.setValue(elem.getValue());
        	me.setReadOnly(elem.dom.readOnly);
        	me.setDisabled(elem.dom.disable);
        	me.render(elem.dom.parentNode);
        	var id = elem.id;
        	elem.dom.parentNode.removeChild(elem.dom);
        	me.inputEl.dom.setAttribute("id", id);
        }else
        // ここまで

        if (me.renderTo) {
            me.render(me.renderTo);
            // EXTJSIV-1935 - should be a way to do afterShow or something, but that
            // won't work. Likewise, rendering hidden and then showing (w/autoShow) has
            // implications to afterRender so we cannot do that.
        }

これで、Strutsタグで書いたフォームにExt JSスタイルを当てることができた

Ext.create("Ext.form.field.Date", {
    applyTo: "inputタグのid"
});

けど、Ext JSStrutsの組み合わせでの正しい使い方ってどんなんなの?誰か教えてください!!

Linux(Ubuntu14)版PHP5.5でImage_Graphを使うと透過色やアルファチャネルが使えない件

前回やっとこさ、PHPSQL Server接続がうまくいったんで、移行する予定のシステムを検証した。

やはり手を加えないと正常に動作しなくって、前の上司が作ったコードが
めちゃくちゃ汚いコードだからか全然動かない。ホント汚い。
scriptタグ内のjavascript部分にphpタグがそのまま出力されてたりするし。
しかも<?phpが勝手に<?で出力される!!ウケるんだけど!!

まあ、どういう対処したか忘れたけど、こういう仕様もない修正を繰り返しながら作業していた。

あるシステムでグラフを表示する機能があって、そのソースはpearでインストールするような
外部モジュール?を使っていた。
それが、Image_Graphというやつ。

今稼働中の/usr/share/phpから使ってそうなやつをrsyncでごっそり新サーバへコピー。
それでグラフ表示するページを動かしたら、gdとかいうエクステンションが必要らしい。

まあよい。手っ取り早くaptでgetする

apt-get install php5-gd
service apache2 restart

gdが入りました。これでどうや!!とページをリフレッシュすると折れ線グラフの各点の
値を表示する部分とか、グラフ内の最大、最小のエリアを表示する部分が透けてないの!!

なにそれー。意味わかんない。前回といい、何で同じコードで同じように動かないの?
JavaでもPOIとかではあったけど、そんなになかったけどなぁ。あーうっとおしい。

普通はこういうコードで透過色になったり、アルファチャネルのついた色を付けたりできるらしい。

$Marker->setBackgroundColor('');      // 透過色
$Marker->setBackgroundColor('green@0.2');      //透けた緑

@移行の数値を0.0とかにしてみたりしたけど何も変わらず。
とりあえずImage_Graphの透過色を扱う部分を、ざらっと見ていった

そしたら、このソースの部分で透過色が使える条件が書いてあった。
/usr/share/php/Image/Canvas/GD/PNG.php

60         if ((isset($param['transparent'])) && ($param['transparent']) &&
61             ($this->_gd2)
62         ) {
63             if ($param['transparent'] === true) {
64                 $transparent = '#123ABD';
65             } else {
66                 $transparent = $param['transparent'];
67             }
68             $color = $this->_color($transparent);
69             $trans = ImageColorTransparent($this->_canvas, $color);

Canvasを作る時のコンストラクタのパラメータのmapにtransparent=>trueで追加して、
_gd2がtrueでないといけないと。

transparentのパラメータはドキュメントになかったから無視した。高さと幅の指定しかなかった気がする。

_gd2がtrueになる条件はここ
/usr/share/php/Image/Canvas/GD.php

 145     function Image_Canvas_GD($param)
 146     {
 147         include_once 'Image/Canvas/Color.php';
 148 
 149         parent::Image_Canvas_WithMap($param);
 150 
 151         $this->_gd2 = ($this->_version() == 2);


修正したのはここ

1761     function _version()
1762     {
1763         $result = false;
1764         if (function_exists('gd_info')) {
1765             $info = gd_info();
1766             $version = $info['GD Version'];
1767         } else {
1768             ob_start();
1769             phpinfo(8);
1770             $php_info = ob_get_contents();
1771             ob_end_clean();
1772 
1773             if (ereg("<td[^>]*>GD Version *<\/td><td[^>]*>([^<]*)<\/td>",
1774                 $php_info, $result))
1775             {
1776                 $version = $result[1];
1777             } else {
1778                 $version = null;
1779             }
1780         }
1781 
1782 // ここ
1783         if ("1" == substr($version, 0, 1)) {
1784             return 1;
1785 // ここ 
1786         }elseif ("2" == substr($version, 0, 1)) {
1787             return 2;
1788         } else {
1789             return 0;
1790         }

まえのバージョン判断はこんな感じ。「2.1.1」とかはバージョン1と判断されてしまう。

if (ereg('1\.[0-9]{1,2}', $version)) {
	return 1;
} elseif (ereg('2\.[0-9]{1,2}', $version)) {
	return 2;
} else {
	return 0;
}

gd_info()で取得したgdのバージョンが、うちの環境でインストールされたものと
合ってなかった。うちでのバージョン表記はこんな感じ。

これの修正で透過色はアルファチャネルが使えるようになった。

Linux版PHP5.4以降のODBCエクステンションがsegmentation fault

院内独自開発システム用のサーバがDebianでPHP5.2を使ってる。PostgreSQLも入っている。
俺の前の上司が一人で作った環境。

前にサーバトラブルがあってPostgreSQLのデータファイルが壊れているらしく自動バキュームが
全く働いてない。
どうもpostgresデータベースというかシステムデータベースの、忘れたけどpg_xxxxxxとか
いうテーブルが開けないみたい。

そのせいか、PostgreSQLディレクトリの容量がデータ量に見合ってないぐらいデカい。

まあ、そのサーバのVMイメージをコピーして、ググっていろいろやったが全くダメ。
なんか壊れている領域を強制的にゼロで埋めるコマンドとかもやったが全く改善しない。
これは、サーバ移行せんといかんかなー。と考えたのが先週の月曜日。

仮想サーバにCentOS7サーバを準備して、apache/php/pgsql/unixODBC/FreeTDSなど
いろいろインストール。画像やphpファイルはrsyncで持ってきた。さあ動作確認。
あるシステムの最初のページは表示された。
が、次のページに行くと「受信したデータが・・・」みたいなエラー。

apacheのerror_logを見るとsegmentation faultでPHPが落ちてるじゃないですか。

調べてみると、たわいもないコードのところで落ちている。
こんなコードでも落ちる。

<?php
        $con = odbc_connect("xxx.xxx.xxx.xxx", "xxx", "xxx");
        if(empty($con)) {
                echo "error";
                die;
        }else{
                echo "connecting success";
        }
        echo "\n";
		// STAFFはテーブルでもビューでも落ちる
        $sql = "SELECT KANJINAME FROM STAFF";
        $result = odbc_exec($con, $sql);
        while(odbc_fetch_row($result)) {
                echo "test:";
                echo odbc_result($result, "KANJINAME");
                echo "\n";
        }

SQL Serverのデータを取りにいくところで落ちる。稼働中のサーバでは動いているし、
このSQL ServerJavaのシステムでも使っているのでデータが悪い訳ではないと思う。
ちなみに、PDOでやってみると落ちはしないがodbcで落ちるデータを取ると空文字になる。

いろいろやってみると、SQL ServerでカラムがVARCHAR(24)に全角8文字入れると落ちないが
VARCHAR(23)に全角8文字入れると落ちることに気づいた。けど、ググっても全然情報出てこない。

しょうがないから、PHP5.5、5.6とかubuntu14でも試したけど状況かわらず。
これは、PHPODBCのレイヤが悪いんじゃなかろうかと。
どうせ暇だし、PHPのソース改造してでもやるかい。あー、けどすごいめんどくせー。

とりあえず、gdbでトレースとった結果がこれ。

#0  __memcpy_sse2_unaligned ()
    at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:116
#1  0x00000000006c8d58 in _estrndup ()
#2  0x00007f7bbbcd0f4f in zif_odbc_result (ht=<optimized out>, 
    return_value=0x7f7bbeea3630, return_value_ptr=<optimized out>, 
    this_ptr=<optimized out>, return_value_used=<optimized out>)
    at /root/php_dev/php5.5-201409112230/ext/odbc/php_odbc.c:2186
#3  0x00000000006dd9bb in dtrace_execute_internal ()
#4  0x000000000079da15 in ?? ()
#5  0x0000000000717748 in execute_ex ()
#6  0x00000000006dd8b9 in dtrace_execute_ex ()
#7  0x00000000006ef340 in zend_execute_scripts ()
#8  0x000000000068f225 in php_execute_script ()
#9  0x000000000079f9ee in ?? ()
#10 0x0000000000461de0 in main ()

あー、なんかメモリ破壊してる。まあ、どこでアロックするサイズ間違えてるか知らんが
多めに取れば問題ないでしょ。と思って、メモリアロックしてる箇所を探すことにした。

ソースは適当にこれを使った
http://snaps.php.net/php5.5-201409112230.tar.gz

メモリ破壊している部分はここ(修正後の行数だから大体の目安で見て)
RETURN_STRINGLマクロで_estrndupが使われていた。そこで落ちる。ちなみに_estrndupで
アロックするサイズは第2引数だった。

2189                         if (result->values[field_ind].vallen == SQL_NULL_DATA) {
2190                                 RETURN_NULL();
2191                         } else {
2192                                 RETURN_STRINGL(result->values[field_ind].value, result->values[field_ind].vallen, 1);
2193                         }

アロックしている部分はここだった

 989                         case SQL_CHAR:
 990                         case SQL_VARCHAR:
 991 #if defined(ODBCVER) && (ODBCVER >= 0x0300)
 992                         case SQL_WCHAR:
 993                         case SQL_WVARCHAR:
 994                                 colfieldid = SQL_DESC_OCTET_LENGTH;
 995 #else
 996                                 charextraalloc = 1;
 997 #endif
 998                         default:
 999                                 rc = SQLColAttributes(result->stmt, (SQLUSMALLINT)(i+1), colfieldid,
1000                                                                 NULL, 0, NULL, &displaysize);
1001                                 /* Workaround for Oracle ODBC Driver bug (#50162) when fetching TIMESTAMP column */
1002                                 if (result->values[i].coltype == SQL_TIMESTAMP) {
1003                                         displaysize += 3;
1004                                 }
1005 
1006                                 if (charextraalloc) {
1007                                         /* Since we don't know the exact # of bytes, allocate extra */
1008                                         displaysize *= 4;
1009                                 }
1010 /********* ここから *************/
1011 #if 1 /* 修正後 */
1012                                 result->values[i].value = (char *)emalloc(displaysize + 4);
1013                                 rc = SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value,
1014                                                         displaysize + 4, &result->values[i].vallen);
1015 #else /* 修正前 */
1016                                 result->values[i].value = (char *)emalloc(displaysize + 1);
1017                                 rc = SQLBindCol(result->stmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, result->values[i].value,
1018                                                         displaysize + 1, &result->values[i].vallen);
1019 #endif
1010 /********* ここまで *************/

1008行目で(カラムサイズ×4)バイト分のメモリを取ろうとしているけど、修正前の1018行目では
1バイト分しか余分に取ってなかったが、修正後は4バイト余分に取るようにした。
この修正後、ビルドしてodbc.soだけ入れ替えたらテーブルのVARCHAR(24)は落ちなくなった。
ちなみに、phpソースからphp_odbc.soを作成する方法は以下のコマンド

./configure --with-unixODBC=shared --enable-so
make
cp -f modules/odbc.so /usr/lib/php5/20121212/

が、ビューではやっぱり落ちる。まあ、今考えたら当たり前か。この修正では_estrndupに渡される値は
何もかわらないし。

テーブルの時はSQLBindColの第6引数のvallenにカラムサイズが入ってくるけど、ビューの時は-4が
入ってくる。-4はSQL_NO_TOTALを指すが何のこっちゃさっぱりだわ。
ビューだからカラムサイズとれないのかなー?

まあ、RETURN_STRINGLマクロの第2引数に−4(SQL_NO_TOTAL)が指定されているから
落ちるということがわかった。
で、このように修正した。

2189                         if (result->values[field_ind].vallen == SQL_NULL_DATA) {
2190                                 RETURN_NULL();
2191                         /********* ここから *************/
2192                         }else if (result->values[field_ind].vallen == SQL_NO_TOTAL) {
2193                                 RETURN_STRINGL(result->values[field_ind].value, strlen(result->values[field_ind].value), 1);
2194                         /********* ここまで *************/
2195                         } else {
2196                                 RETURN_STRINGL(result->values[field_ind].value, result->values[field_ind].vallen, 1);
2197                         }

めちゃくちゃ姑息!!でもこれ以外の方法が思いつかなかった。
configure/makeしてodbc.so入れ替えたら落ちなくなった。まあいいや。
どうせ同じ職場の人間しか使わないし。

けど、この調査ホントに疲れた!!近くに相談できる人でもいればなー。

けど、この現象ってうちの会社の環境が特殊だからなのか?WindowsPHPでは試してないけど、
LinuxPHPSQL Server使うことが珍しいの?
PHPerの人は、こういうこと普通にしとるんだろうか・・・。誰か教えてください