<?xml version="1.0" encoding="utf-8"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="ja">
<title>dTblog | design and programming</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/" />
<modified>2008-10-14T14:49:27Z</modified>
<tagline>デザインから開発まで色んな局面でWebと関わる人のよもやま話。</tagline>
<id>tag:www.deftrash.com,2008:/blog//3</id>
<generator url="http://www.movabletype.org/" version="3.2-ja-2">Movable Type</generator>
<copyright>Copyright (c) 2008, deftrash</copyright>
<entry>
<title>Excel 形式の帳票ツール</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/10/excel_report.html" />
<modified>2008-10-14T14:49:27Z</modified>
<issued>2008-10-14T14:36:22Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.498</id>
<created>2008-10-14T14:36:22Z</created>
<summary type="text/plain">Java で Excel 形式の帳票を作成するのに、いままでは Jakarta ...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>java</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>Java で Excel 形式の帳票を作成するのに、いままでは <a href="http://poi.apache.org/">Jakarta POI </a> の HSSF でゴリゴリとコーディングしていたのだけど、レイアウトに対して決して直感的なソースじゃないし、フォーマット変更に対する保守性も低いので、何か別の方法はないかと思っていた。</p>

<p>とりあえず有名どころと思って、<a href="http://www.jasperforge.org/jaspersoft/opensource/business_intelligence/ireport/">iReport</a> と <a href="http://www.jasperforge.org/jaspersoft/opensource/business_intelligence/jasperreports/">JasperReport </a> を触ってみたんだけど、ダメ。</p>

<p>iReport は非常に優れた帳票デザイナーで、これがフリーだということに驚愕すること間違いなしなんだけど、いかんせん、出てくる Excel が使えない。見た目は確かにレイアウトどおりなんだろうけど、行と列がグチャグチャ。出力後に人間が加工するような感じではない。PDF 形式で出力するのであれば、かなり優秀なので、選択肢として検討する余地はあるけど。</p>

<p>やっぱり、HSSF でゴリゴリ行くしかないのかな。レイアウトした xls を読み込んで、データを書き出して出力するようにすれば、iReport のように直感的でないけど、そこそこレイアウト変更にも柔軟に対応できそうな気もする。（xlsとHSSFを結びつけるための、レイアウト情報をどう管理するかっていう問題は考えないといけないけど）</p>

<p><a href="http://www.eclipse.org/birt/phoenix/">BIRT </a>も Excel 出力ができるらしい。こちらは、どうなんだろう。今度さわってみよう。</p>]]>

</content>
</entry>
<entry>
<title>bashでヒアドキュメントをリダイレクト</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/10/bash.html" />
<modified>2008-10-05T08:52:34Z</modified>
<issued>2008-10-05T08:46:22Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.497</id>
<created>2008-10-05T08:46:22Z</created>
<summary type="text/plain">ヒアドキュメントを使って、ファイルに内容を書き出そうと思ったんだけど、どこにリダ...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>linux</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>ヒアドキュメントを使って、ファイルに内容を書き出そうと思ったんだけど、どこにリダイレクトを書くのか分からなくて苦戦した。ううう。</p>

<p>正解はこちら。最初に指定しておくのか。なるほど。</p>

<pre class="code">
cat << HERE >> /tmp/hoge 2>&1
xxxxxx
yyyyyy
zzzzzz
HERE
</pre>

<p>bashのヒアドキュメントは、メール送信以外で使ったのは、これが初めてかもしれない。便利な仕組みだけど、あんまり用途が思いつかない。</p>]]>

</content>
</entry>
<entry>
<title>validwhen では小数の大小比較が行えない</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/09/validwhen.html" />
<modified>2008-09-04T07:49:46Z</modified>
<issued>2008-09-04T07:31:46Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.496</id>
<created>2008-09-04T07:31:46Z</created>
<summary type="text/plain">struts の validwhen では、小数付きの数値比較が正しく行われない...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>java</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>struts の validwhen では、小数付きの数値比較が正しく行われない。</p>

<p>ソースを追ってみたところ、ValidWhenParser クラスで比較処理をしていたのだけど、これが int 型で比較できる場合は数値として比較、それ以外はすべて String で比較するという処理になっていた。えー、整数だけですか。全然かゆいところに手が届いていない！</p>

<p>これのタチ悪いところは、小数値であっても比較処理自体は行われる点。そう、文字列として比較して、その結果たまに正しい結果を返してしまう。厄介だ。</p>

<p>例えば、start と end という数値入力するフィールドがあって、その大小関係が逆転しないように入力チェックをするとき、</p>

<pre class="code">
((start == null) or (*this* <= end))
</pre>

<p>と validwhen 式を書いて、</p>

<pre class="code">
private String start = "9.5";
private String end = "11";
</pre>

<p>とすると、文字列での比較となり、1より9の方が後になるってんで、大小関係が正しいにも関わらずエラーとして検証に引っかかってしまう。</p>

<pre class="code">
private String start = "2.5";
private String end = "5";
</pre>

<p>という場合は、小数があっても正しい検証結果を返すので、問題なし。こんな感じで一見すると正しく動いているようにみえるので、むーって感じ。ハマリました。</p>

<p>そんなわけで、validwhen は整数の比較と、文字列の比較のときのみ使用するようにしなくちゃいけないです。いや、もう文字列に限定して使用するものだと捉えた方が良いかもしれない。そして数値比較用には、自前で検証メソッドを作成してしまった方が、間違いがなくて良さそう。（または ValidWhenParser の処理そのものを小数対応に直しちゃうのもアリ）</p>

<p>BigDecimal で比較、いやせめて Double で比較してくれれば良かったのに…。</p>]]>

</content>
</entry>
<entry>
<title>Tiles Hack のステップアップ</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/08/tiles_hack.html" />
<modified>2008-08-12T08:05:52Z</modified>
<issued>2008-08-12T07:40:53Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.495</id>
<created>2008-08-12T07:40:53Z</created>
<summary type="text/plain">Tiles Hack だなんて大げさだ。 Struts の Tiles プラグイ...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>java</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>Tiles Hack だなんて大げさだ。</p>

<p>Struts の Tiles プラグインの挙動を変更したくなった。動的に definition を追加したい。どこから手を付けて良いものか悩んでいたのだけど、DefinitionsFatory を自作のものに変更できることが判明したので、これで対応した。</p>

<p>struts-config.xml で Tiles プラグインを設定している箇所で、definitions-factory-class プロパティに、自作の Factory クラスを指定してあげるだけ。</p>

<pre class="code">
  &lt;plug-in className="org.apache.struts.tiles.TilesPlugin"&gt;
    &lt;set-property property="definitions-factory-class" value="com.deftrash.struts.OriginalI18nFactorySet" /&gt;
    &lt;set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/&gt;
    &lt;set-property property="moduleAware" value="true"/&gt;
  &lt;/plug-in&gt;
</pre>

<p>definitions-factory-class を指定しない場合は、デフォルトで、org.apache.struts.tiles.xmlDefinition.I18nFactorySet が使用される。tiles-defs.xml を読み込んで、ComponentDefinition を生成するクラスだ。</p>

<p>動的に definition を追加するために、自作 Factory を、この I18nFactorySet を継承して作成。createDefaultFactory メソッドをオーバーライドして改造することにした。</p>

<pre class="code">
    /* (非 Javadoc)
     * @see org.apache.struts.tiles.xmlDefinition.I18nFactorySet#createDefaultFactory(javax.servlet.ServletContext)
     */
    protected DefinitionsFactory createDefaultFactory(ServletContext servletContext)
            throws DefinitionsFactoryException, FileNotFoundException {
<br />
        DefinitionsFactory factory = super.createDefaultFactory(servletContext);
<br />
        // TODO 動的にComponentDefinitionを追加する処理をここに
<br />
        return factory;
</pre>

<p>たったこれだけで、望むべき動作が得られました。動的に definition を追加する以外のニーズがあっても、ここを起点にいくらでも改造できそうな感じ。まあ、I18nFactorySet クラスで xml を読み出すところが private になっているのが、扱いづらいけど。</p>

<p>すでに struts は枯れて細かいところまで見ずにいたけど、実際に手を入れていこうとすると、改造しやすいように窓口が開いていて、風の通しがすごい良いことに気付く。こういう懐の深いデザインは、見習っていきたいですね。</p>]]>

</content>
</entry>
<entry>
<title>Strutsで独自のModuleConfigFactoryを利用する方法</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/07/strutsmoduleconfigfactory.html" />
<modified>2008-07-24T14:53:00Z</modified>
<issued>2008-07-24T14:35:59Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.494</id>
<created>2008-07-24T14:35:59Z</created>
<summary type="text/plain">struts-config.xml で定義した内容を動的に変更したくなって、Ac...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>java</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>struts-config.xml で定義した内容を動的に変更したくなって、ActionConfig を改造する方法を考えていたんだけど、大元の ModuleConfigFactory を切り替えて、独自の ModuleConfig を生成する Factory クラスを用意すれば良いことが分かった。なるほどー、よくできてるなー。</p>

<p>web.xml で configFactory パラメータに、独自の ModuleConfigFactory のクラスを指定してあげればOK。</p>

<pre class="code">
  &lt;servlet&gt;
    &lt;servlet-name&gt;action&lt;/servlet-name&gt;
    &lt;servlet-class&gt;org.apache.struts.action.ActionServlet&lt;/servlet-class&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;config&lt;/param-name&gt;
      &lt;param-value&gt;/WEB-INF/struts-config.xml&lt;/param-value&gt;
    &lt;/init-param&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;configFactory&lt;/param-name&gt;
      &lt;param-value&gt;com.deftrash.OriginalModuleConfigFactory&lt;/param-value&gt;      
    &lt;/init-param&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;debug&lt;/param-name&gt;
      &lt;param-value&gt;2&lt;/param-value&gt;
    &lt;/init-param&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;detail&lt;/param-name&gt;
      &lt;param-value&gt;2&lt;/param-value&gt;
    &lt;/init-param&gt;
    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
  &lt;/servlet&gt;
</pre>

<p>いろいろ調べてようやく分かったんだけど、よくよく考えてみると SAStruts の仕組みを見れば一発で分かったんだよなあ。ううう。</p>

<p>そして、もともとはフォワード先のパスを変えたいだけだったので、それなら Filter で ActionConfig を見てごにょごにょやった方がシンプルだったかも。この方法は、少し大げさな感じもする。こういう手段があるのが分かっただけで、前進ですが。</p>

<p>ActionServlet クラスのソースを見ると、他にも細かな制御ができるようになっているのが分かる。このあたりを押さえておくと、プロジェクトに応じて Struts をより柔軟にコントロールできる。知らないって、スゴイ損ですねえ。ふぅ。</p>]]>

</content>
</entry>
<entry>
<title>PostgreSQLで半角カナを全角カナに変換する関数</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/07/h2z_kana.html" />
<modified>2008-07-15T14:03:03Z</modified>
<issued>2008-07-15T13:49:41Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.493</id>
<created>2008-07-15T13:49:41Z</created>
<summary type="text/plain">半角カナでの登録を拒否するために、自動で全角カナに変換したいときってあるじゃない...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>db</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>半角カナでの登録を拒否するために、自動で全角カナに変換したいときってあるじゃないですか。アプリケーション側で、AOPやFilterといった仕組みで変換してあげる方がスマートだと思ってるんだけど、DB側でやらないといけないケースもあるでしょう。</p>

<p>ということで、こんな関数を用意してみました。</p>

<pre class="code">
CREATE OR REPLACE FUNCTION h2z_kana(text) 
  RETURNS text AS
$BODY$
    DECLARE
        zenkaku alias FOR $1;
        result text;
        i int;
<br />
        zt varchar[] = ARRAY['ガ', 'ギ', 'グ', 'ゲ', 'ゴ', 'ザ', 'ジ', 'ズ', 'ゼ', 'ゾ', 'ダ', 'ヂ', 'ヅ', 'デ', 'ド', 'バ', 'ビ', 'ブ', 'ベ', 'ボ', 'パ', 'ピ', 'プ', 'ペ', 'ポ', 'ヴ'];
        ht varchar[] = ARRAY['ｶﾞ', 'ｷﾞ', 'ｸﾞ', 'ｹﾞ', 'ｺﾞ', 'ｻﾞ', 'ｼﾞ', 'ｽﾞ', 'ｾﾞ', 'ｿﾞ', 'ﾀﾞ', 'ﾁﾞ', 'ﾂﾞ', 'ﾃﾞ', 'ﾄﾞ', 'ﾊﾞ', 'ﾋﾞ', 'ﾌﾞ', 'ﾍﾞ', 'ﾎﾞ', 'ﾊﾟ', 'ﾋﾟ', 'ﾌﾟ', 'ﾍﾟ', 'ﾎﾟ', 'ｳﾞ'];
    BEGIN
<br />
        result = zenkaku;
<br />
        -- 2バイトで変換
        FOR i IN 1..26 LOOP
            result = replace(result, ht[i], zt[i]);
        END LOOP;
<br />
        -- 1バイトで変換
        result = translate(result,
                   ' ｱｲｳｴｵｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖﾗﾘﾙﾚﾛﾜｦﾝｧｨｩｪｫｯｬｭｮﾜｲｴｶｹｰ､｡･｣｢ﾞ,<.>/?_}]*:+;{[~@|\\`^=-)(&%$#"!',
                   '　アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲンァィゥェォッャュョヮヰヱヵヶー、。・」「゛，＜．＞／？＿｝］＊：＋；｛［￣＠｜￥｀＾＝－）（＆％＄＃”！'
              );
<br />
        RETURN result;
    END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE;
</pre>

<p>ガやザのような濁音のものは、半角カナになるとカと濁点の合計2バイトになる。まずはじめに変換テーブルを用いてそれらを置換してから、残る半角カナを一括して置換してます。記号の置換処理のところは、不要なら除けばOK。でも、大体のケースであっても困らないと思いますけど。</p>

<p>あとは、この関数を使って UPDATE するトリガーを、INSERT と UPDATE 時に発動するように定義してあげれば、冒頭の件は対応できると思います。</p>

<p>DBから値を引っ張ってきて、そのままメール送信するようなケースで、半角カナが含まれてたらイヤンなときにも活用できるかな。そのケースでは、機種依存文字全般の対応もあるので、送信プログラム側でもろもろを処理した方が、スッキリするかしら？</p>

<p>ま、ひとつの参考までに。</p>]]>

</content>
</entry>
<entry>
<title>NULL文字を削除する</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/07/trnullchar.html" />
<modified>2008-07-09T04:17:39Z</modified>
<issued>2008-07-09T03:56:43Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.492</id>
<created>2008-07-09T03:56:43Z</created>
<summary type="text/plain">csvデータをpostgresqlにインポートしようとしたら、エラーが。 ERR...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>linux</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>csvデータをpostgresqlにインポートしようとしたら、エラーが。</p>

<pre class="code">
ERROR:  invalid byte sequence for encoding "SJIS": 0x00
</pre>

<p>NULL文字が含まれているのが、納得いかないようす。NULL文字は目で見えない制御文字なので、どうやって削除したもんかなーと思ったら、trコマンドで簡単に除去できました。</p>

<pre class="code">
$ cat src.csv | tr -d "\000" > dest.csv
</pre>

<p>これでインポートはバッチリでした。</p>]]>
<![CDATA[<p>今回のケースは、SQLServer のデータを postgresql に移行する作業中に起こったもの。この両者でデータ移行する際は、色々と面倒なことが多い。</p>

<p>SQLServer が吐き出してくれる csv の文字コードは Unicode (UCS-2) なので、この文字コードを変換しつつ、NULL文字も削除しつつ、postgresql に移行する場合は、次のような感じ。</p>

<pre class="code">
$ iconv -f ucs-2 -t cp932 src.csv | tr -d "\000" > dest.csv
$ psql target-db
# \encoding SJIS
# COPY table FROM 'dest.csv' WITH CSV NULL AS 'NULL';
</pre>

<p>SQLServer では datetime 型のカラムが NULL の場合、NULLという文字を出力するんだけど、これを postgresql の timestamp 型に copy しようとすると、エラーになってしまう。COPY時に NULL AS 'NULL'  を付けることで、NULL という文字列を NULL として扱わせることができるので、timestamp型を NULL でインサートできるようになります。</p>

<p>お疲れさまでした。</p>]]>
</content>
</entry>
<entry>
<title>PostgreSQLの配列を使って文字列を集約をする方法</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/07/postgresql.html" />
<modified>2008-07-03T15:14:45Z</modified>
<issued>2008-07-03T14:30:17Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.491</id>
<created>2008-07-03T14:30:17Z</created>
<summary type="text/plain">1対N（OneToMany）の関係にある2つのエンティティについて、N個の文字列...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>db</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>1対N（OneToMany）の関係にある2つのエンティティについて、N個の文字列を集約する必要があって、方法を考えてみた。</p>

<p>次のような部署マスタと社員マスタを例にとって、説明します。</p>

<p><img alt="20080703_ER.png" src="http://www.deftrash.com/blog/archives/contents/20080703_ER.png" width="374" height="97" /></p>

<p>これに対して、次のような表を出すという要件。</p>

<table class="basic">
 <colgroup><col width="150" /><col width="150" /></colgroup>
 <thead>
  <th>部署</th>
  <th>所属社員</th>
 </thead>
 <tbody>
  <tr>
   <th>営業1課</th>
   <td>佐藤<br />山下<br />高山</td>
  </tr>
  <tr>
   <th>営業2課</th>
   <td>大野<br />池本<br />野崎</td>
  </tr>
 </tbody>
</table>

<p>「2つのエンティティを JOIN すれば良いじゃん」という声もあるけど、単純に結合しちゃうと、結果セットから取り出すとき、もしくは表示時点でプログラムでごにょごにょやらないと、うまくいかない。所属社員の一覧を、改行区切りの文字列として、ひとつの変数に格納できれば、らくちん。要するに、DBから値を取得する時点で、表示するときの形まで持っていきたいという話。</p>

<p>ここの方法については賛否両論あるかもしれないけど、SQLはプログラムが最もシンプルで済むような結果が得られるように書くべきだと思ってます。</p>

<p>話が逸れたけど、じゃあ、それをどうやってやるか、という話ですよ。</p>]]>
<![CDATA[<p>実は以前にも同じような要件について考えたことがあって、そのときは<a href="http://www.deftrash.com/blog/archives/2007/06/sqlsum.html">文字列をSUMする関数</a>を用意した。</p>

<pre class="code">
SELECT d.department_name, sum(e.employee_name)
FROM department d, employee e
WHERE d.department_id=e.department_id
GROUP BY d.department_name;
</pre>

<p>まあ、これでも要件は満たせるんだけど、上のような感じで集約関数を使ってしまうと、レコードの出現順に値を結合することになってしまうので、表示順を制御したい場合には、副問合せで ORDER BY して、それを結合してから GROUP BY するという、面倒くさいことになってしまう。今回のように単純なら良いけど、他にも 1対N の関係にあるエンティティを同じように集約して取得したい、となってくると、使い勝手がよろしくないんですYO。</p>

<p>そこで、今回考えたのが、配列を使った方法。</p>

<pre class="code">
SELECT 
 department_name,
 ARRAY_TO_STRING(
   ARRAY(
      SELECT employee_name FROM employee e
      WHERE e.department_id=d.department_id
      ORDER BY employee_name
   ), '\r\n') AS employee_name
FROM department d;
</pre>

<p>自己相関サブクエリで所属社員の一覧を取得して、それを一旦配列化する。んでもって、その配列を改行コード区切りの文字列に分解する、ということをしてます。これだと見ての通り、ORDER BY も気軽に書けるし、やたらめったら結合していくよりも、パフォーマンスも良好。</p>

<p>DBの配列って、ぶっちゃけ普通は使わない。配列型のカラムがあったら、それは多くの場合、正規化に失敗している。大体にして、配列型のカラムは検索のSQLを書くのが面倒くさい。それだったら、正規化して JOIN した方が良い。</p>

<p>でも、こういう風にSQL内部に隠蔽される形での使用だったら、問題ないでしょう、と。自分で書いておきながらなんですが、配列にこういう使い方もあるんだなあ、といったところ。</p>

<p>それにしても、こういう要件のときの一般的なパターンはどんな感じなんだろう。やっぱりプログラムでゴリゴリやるのが正攻法なのかなあ。「こういう風にやると良いよ」とか「こんな感じでやってます」っていうのがあったら、教えてエロい人！</p>

<p>お疲れさまでした。</p>]]>
</content>
</entry>
<entry>
<title>FreeBSDのJVMでエラーが起きる</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/07/freebsdjvm.html" />
<modified>2008-07-01T05:19:59Z</modified>
<issued>2008-07-01T05:05:25Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.490</id>
<created>2008-07-01T05:05:25Z</created>
<summary type="text/plain">FreeBSD のサーバに、Java5 と Tomcat5.5 を乗っけて起動し...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>java</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>FreeBSD のサーバに、Java5 と Tomcat5.5 を乗っけて起動したら、OOME で落ちてしまった。ログを見ると、かつて見たことのない内容が。</p>

<pre class="code">
Exception in thread "CompilerThread0" java.lang.OutOfMemoryError: 
requested 1168080 bytes for Chunk::new. Out of swap space?
</pre>

<p>スワップ領域って何のことだろうか。JVMのなかの話だと思うのだけど。</p>

<p>メモリ関連ということで、-Xmx や -Xss などのパラメータをいじるものの状況は改善せず。んで、ひとつひとつ除いていったら分かりました。</p>

<pre class="code">
CATALINA_OPTS="-server"
</pre>

<p>というように、サーバクラスを指定していたのだけど、これが原因だった。FreeBSD全部がそうなのか分からないけど、今回のケースではクライアントVMでしか起動できないという罠。うー、しびれるなあ。</p>

<p><a href="http://sdc.sun.co.jp/java/docs/j2se/1.5.0/ja/docs/ja/guide/vm/server-class.html" target="_blank">サーバクラスマシンの検出</a>にあるとおり、Java5からはサーバ環境に応じて自動でVMを切り替えてくれるということで、下手に指定なんてしなくても良かったんですね。（まあ、FreeBSDのJVMでも互換性があるのかは分かりませんが…）</p>

<p>Linux の方が上だとか言うつもりは毛頭ないけど、個人的には FreeBSD より Linux の方が良いなあ。習慣もあるんだろうけども、エンタープライズ用途だと何かと情報が得やすい Linux や Solaris でないと、ちょっとひるんでしまいますねえ。</p>]]>

</content>
</entry>
<entry>
<title>ロープ・データ構造</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/06/java_rope.html" />
<modified>2008-06-04T05:28:33Z</modified>
<issued>2008-06-04T05:15:24Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.489</id>
<created>2008-06-04T05:15:24Z</created>
<summary type="text/plain">ロープ： 理論と実践 String や StringBuilder に代わる効率...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>java</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p><a href="http://www.ibm.com/developerworks/jp/java/library/j-ropes/">ロープ： 理論と実践</a></p>

<p>String や StringBuilder に代わる効率的なデータ構造「ロープ」。java で大量文字列を処理する場合の、新しい解だ。ストリングに対してロープとな。なるほど。</p>

<p>意味が理解できたところで、さて何に使ったら良いもんか。「顕著なパフォーマンスの改善が期待できるのは、大規模なストリングを大々的に変更するような場合のみです」と言われても、それこそ例として挙げられているテキスト・エディタのようなものしか思い浮かばない。そもそも、単一のストリングがそこまで大規模になったケースを知らない。うーん。</p>

<p>ま、アイデアとしてストックしておけば、他でも使えるかな。</p>

<p>ちなみに、「O(n)」とか「O(log n)」とかをビッグオー記法と呼ぶのを、この記事で初めて知りました。これ、意味は分かるんだけど、どのくらいのオーダーの差があるのか直感的に分かりづらい…。早く自然数と対数がサラッと比較できるようなビジネスマンになりたいです。</p>]]>

</content>
</entry>
<entry>
<title>tomcat6 のログ出力を Log4j で行う方法</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/06/tomcat6_log4j.html" />
<modified>2008-06-01T14:11:47Z</modified>
<issued>2008-06-01T13:25:40Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.488</id>
<created>2008-06-01T13:25:40Z</created>
<summary type="text/plain">これで、かなり快適なロギング環境を構築することができました。すごい便利。 事の始...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>java</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>これで、かなり快適なロギング環境を構築することができました。すごい便利。</p>

<p>事の始まりは、tomcat6 のログローテションを自前で行おうとしたことだった。tomcat6 では、デフォルトで日付付きのログファイルを出力するのだけど、それだと自前のローテーション処理とぶつかってしまう。これはどげんかせんといかん、と言うわけで、<a href="http://www.oki.com/jp/oss/document/tomcat/tomcat-6.0.14/build/tomcat-docs/logging.html" target="_blank">公式マニュアル</a>を見たら、Log4J を使う方法が書いてあったので、「これだ！」と。</p>

<p>しかし、Log4J の効果はローテーションに留まらず。ログ出力内容の細かなコントロールも可能になったことで、運用上必要なログのみを出力するようにできたり、かなり嬉しいことに。やってて良かった公文式。（違う）</p>

<p>以下、Log4J 導入までの流れ。（ほとんどマニュアルと同じですが）</p>]]>
<![CDATA[<p>今回、公式マニュアルのほかに、以下のサイトを参考にしました。</p>

<p><a href="http://moyolab.blog57.fc2.com/blog-entry-66.html" target="_blank">Tomcat 6.0 の標準ログを Log4j に変更 MOYO Laboratory</a></p>

<p>まず、こちらのサイトにもあるように、tomcat-juli.jar と tomcat-juli-adapters.jar を生成します。ビルド済みの JAR も落とせるようになっているので、そちらを利用するのも可。（ちゃんと動作しました）</p>

<p>生成した jar をそれぞれ既存のものに上書きコピー。</p>

<pre class="code">
# cp tomcat-juli.jar ${catalina.home}/bin/
# cp tomcat-juli-adapters.jar ${catalina.home}/lib/
</pre>

<p>続いて、log4j.properties を作成。</p>

<pre class="code">
# vi ${catalina.home}/conf/log4j.properties
</pre>

<p>今回はローテーションを別プロセスで行うので、ひたすら catalina.out にログを吐き続けるように設定。ConversionPattern などは好きにやっちゃってください！</p>

<pre class="code">
log4j.rootLogger=INFO, R
log4j.appender.R=org.apache.log4j.FileAppender
log4j.appender.R.File=${catalina.home}/logs/catalina.out
log4j.appender.R.Append=true
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%-5p: %d [%t] %m%n
<br />
log4j.logger.org.apache.catalina.core.ContainerBase.[Catalina].[localhost]=INFO, R
log4j.logger.org.apache.catalina.core=INFO, R
log4j.logger.org.apache.catalina.session=INFO, R
</pre>

<p>いま作成した log4j 設定ファイルを読み込むように、catalina.sh を修正。JAVA_OPTS に次の一文を加えます。（パスは各環境に合わせて読んでください）</p>

<pre class="code">
JAVA_OPTS="$JAVA_OPTS" -Dlog4j.configuration=file:/usr/local/tomcat/conf/log4j.properties
</pre>

<p>これで完了、と言いたいところなんだけど、このままだと既存のLogging機構も動いてしまう。それだと元々のコンセプトが崩れてしまうので、これまた起動スクリプトをいじります。170行目付近を、以下のようにコメントアウト。</p>

<pre class="code">
# Set juli LogManager if it is present
#if [ -r "$CATALINA_BASE"/conf/logging.properties ]; then
#  JAVA_OPTS="$JAVA_OPTS "-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager" "-Djava.util.logging.config.file="$CATALINA_BASE/conf/logging.properties"
#fi
</pre>

<p>これで起動すると、log4j.properties の設定にしたがって、catalina.out にログがバンバン吐き出されるはずです。起動ログを tail してワクワクすると良いと思います。</p>

<p>今回の例には書きませんでしたが、この対応における一番の恩恵は、カテゴリごとに出力レベルを変更できる Log4J の機構を使えることができたことです。</p>

<p>DEBUG は開発用のログではあるんですが、実は運用フェーズでも、一時的に一部のDEBUGメッセージはログに出力したいなんてこと、あるじゃないですか。Logging だと十把一絡げに level = DEBUG にしないといけないんですが、そうすると余計な DEBUG メッセージも付いてくるわから、ログが肥大化しちゃう…。Log4J では細かな制御ができるので、ログ出力を最小に留めつつ、欲しいところで level = DEBUG にできるので、スゲー便利です。</p>

<p>お疲れさまでした。</p>]]>
</content>
</entry>
<entry>
<title>Linuxの圧縮/解凍コマンドまとめ</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/05/linux_commands.html" />
<modified>2008-05-31T10:00:01Z</modified>
<issued>2008-05-31T09:44:45Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.487</id>
<created>2008-05-31T09:44:45Z</created>
<summary type="text/plain">マイナーな圧縮形式になるとコマンドを忘れがちなので、まとめのメモ。 まずは基本の...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>linux</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>マイナーな圧縮形式になるとコマンドを忘れがちなので、まとめのメモ。</p>

<p>まずは基本の gz 形式から。（さすがに忘れないけど）</p>

<pre class="code">
圧縮
$ gzip filename
解凍
$ gunzip filename.gz
</pre>

<p>続いて、tar.gz (tgz) 形式。（これも忘れないなあ）</p>

<pre class="code">
圧縮
$ tar cvfz dirname.tar.gz dirname
解凍
$ tar xvfz dirname.tar.gz
</pre>

<p>bzip2 (bz2) 形式。</p>

<pre class="code">
圧縮
$ bzip2 filename
解凍
$ bzip2 -d filename.bz2
</pre>

<p>tar.bz2 形式。</p>

<pre class="code">
圧縮
$ tar cvfj dirname.tar.bz2 dirname
解凍
$ tar xvfj dirname.tar.bz2
</pre>

<p>Windowsとのデータやり取りの基本、ZIP形式。</p>

<pre class="code">
圧縮
$ zip filename.zip file1 file2
解凍
$ unzip filename.zip
</pre>

<p>これまたWindowsとデータ交換するとよく出てくるLZH形式。</p>

<pre class="code">
圧縮
$ lha -a filename.lzh file1 file2
解凍
$ lha -e filename.lzh
</pre>

<p>こんなところでしょうか。個人的には、tar.bz2形式とLZH形式の解凍が、いつも一発でいきません。あうあう。どうもオプションが覚えられないんだよなあ。チートシート、チートシート、と。</p>]]>

</content>
</entry>
<entry>
<title>FTP専用ユーザーを作ってみる</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/05/ftpuser.html" />
<modified>2008-05-21T18:10:01Z</modified>
<issued>2008-05-21T14:52:14Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.486</id>
<created>2008-05-21T14:52:14Z</created>
<summary type="text/plain">年に1回くらい需要があることが分かったので、ここらへんでメモしてみる。 # us...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>linux</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>年に1回くらい需要があることが分かったので、ここらへんでメモしてみる。</p>

<pre class="code">
# useradd -s /sbin/nologin -M ftpuser
# passwd ftpuser
# mkdir -p /ftp/userdir
# usermod -d /ftp/userdir ftpuser
# chown ftpuser:ftpuser /ftp/userdir
# chmod 705 /ftp/userdir
</pre>

<p>useradd で /sbin/nologin を指定することで、telnet などでログインできないユーザを作成できる。また、-M オプションを付けると、ホームディレクトリを作成せずにユーザを作成するので、不要なメールボックスが作成されないで済む。FTP先のディレクトリは、usermod で後から設定してあげればOK。</p>

<p>ざっくりこんな感じ。</p>

<p>ただ、qmail が動いていると、メールボックスは無いんだけれども、/etc/passwd にエントリされるだけで、有効なアカウントと判断してしまう。だもんで、例えば ftpuser 宛にメールをすると、ローカル配送してしまう。まあ、メールボックスが無くてエラーになるので、目的を達成してはいるんだけど、"no mailbox" なエラーじゃないので、いただけない。</p>

<p>ここらへん、厳密に対処するのであれば、<a href="http://www.deftrash.com/blog/archives/2005/09/qmail.html">qmail でメールアカウントだけを削除する方法</a>のように、ごにょごにょしてやらないといけない。ちょっと大げさ。</p>

<p>色んなサービスが動いていると、細かいところで厄介になるなー。</p>]]>

</content>
</entry>
<entry>
<title>SSLのパスフレーズ入力を回避する方法</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/05/apache_ssl.html" />
<modified>2008-05-19T14:33:31Z</modified>
<issued>2008-05-19T14:19:59Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.485</id>
<created>2008-05-19T14:19:59Z</created>
<summary type="text/plain">SSLを設定してApacheを立ち上げようとすると、秘密鍵のパスフレーズを問合せ...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>linux</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>SSLを設定してApacheを立ち上げようとすると、秘密鍵のパスフレーズを問合せてくる。</p>

<p>手動で運用している分には問題ないのだけれども、スクリプトで起動するような操作をする場合に面倒くさいことになる。そこで、パスフレーズの入力を回避してみる。</p>

<pre class="code">
# openssl rsa -in server.key -out server.key.nopass
</pre>

<p>これで、元の秘密鍵からパスフレーズ抜きの鍵を生成して、これを SSLCertificateKeyFile に指定してあげれば、次回の起動からはパスフレーズの入力なしで、Apacheを起動できます。わーい、便利。</p>

<p>ただし当然ながら、これはセキュリティ上、好ましくないので、単純に「めんどくせえなあ」ということなら、やめておいた方が良いと思います。そういう意味では、裏技ですね。</p>

<p>ちなみに、</p>

<pre class="code">
# apachectl startssl
</pre>

<p>として起動しようとしたら、怒られてしまった。</p>

<p>Apache2.2からは、startssl などと書かずに、普通に start や graceful でSSL起動ができるようになったとのこと。ほう、こりゃまた便利な世の中ですなあ。</p>

<p>ということで、</p>

<pre class="code">
# apachectl start
</pre>

<p>のコマンド一発で、パスフレーズ入力もなく、SSLを意識せずにApacheの運用が出来るようになりました。お疲れさまでした。</p>]]>

</content>
</entry>
<entry>
<title>複数トリガーの実行順序</title>
<link rel="alternate" type="text/html" href="http://www.deftrash.com/blog/archives/2008/05/multi_trigger.html" />
<modified>2008-05-17T03:08:01Z</modified>
<issued>2008-05-16T03:30:14Z</issued>
<id>tag:www.deftrash.com,2008:/blog//3.484</id>
<created>2008-05-16T03:30:14Z</created>
<summary type="text/plain">PostgreSQLで、同一イベントに複数のトリガーがある場合に、どういう順番で...</summary>
<author>
<name>deftrash</name>
<url>http://www.deftrash.com/</url>
<email>dtadmin@deftrash.com</email>
</author>
<dc:subject>db</dc:subject>
<content type="text/html" mode="escaped" xml:lang="ja" xml:base="http://www.deftrash.com/blog/">
<![CDATA[<p>PostgreSQLで、同一イベントに複数のトリガーがある場合に、どういう順番で実行されるのか気になったので調べてみた。</p>

<blockquote>
<div class="quote">
<a href="http://www.postgresql.jp/document/pg824doc/html/trigger-definition.html">トリガ動作の概要</a>
同一リレーション、同一イベントに対して1つ以上のトリガが定義された場合、トリガはその名前のアルファベット順に発生します。 BEFOREトリガの場合では、各トリガで返される、変更された可能性がある行が次のトリガの入力となります。もし、あるBEFOREトリガがNULLを返したら、操作はその行で中断し、残りのトリガは発生しません。 
</div>
</blockquote>

<p>アルファベット順、これは予想外だった。以前は順番を保証することさえなかったみたいだから、それに比べたら方法があるだけマシなのかもしれないけど、トリガ名を考えないといけないということか。なんか明示的な制御の方法はないのかな。</p>]]>

</content>
</entry>

</feed>