dT*blog

design and programming

validwhen では小数の大小比較が行えない

struts の validwhen では、小数付きの数値比較が正しく行われない。

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

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

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

((start == null) or (*this* <= end))

と validwhen 式を書いて、

private String start = "9.5";
private String end = "11";

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

private String start = "2.5";
private String end = "5";

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

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

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

Posted by dT by 16:31 | Comments (0) | TrackBacks (0)

Tiles Hack のステップアップ

Tiles Hack だなんて大げさだ。

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

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

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

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

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

    /* (非 Javadoc)
     * @see org.apache.struts.tiles.xmlDefinition.I18nFactorySet#createDefaultFactory(javax.servlet.ServletContext)
     */
    protected DefinitionsFactory createDefaultFactory(ServletContext servletContext)
            throws DefinitionsFactoryException, FileNotFoundException {

DefinitionsFactory factory = super.createDefaultFactory(servletContext);
// TODO 動的にComponentDefinitionを追加する処理をここに
return factory;

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

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

Posted by dT by 16:40 | Comments (0) | TrackBacks (0)

Strutsで独自のModuleConfigFactoryを利用する方法

struts-config.xml で定義した内容を動的に変更したくなって、ActionConfig を改造する方法を考えていたんだけど、大元の ModuleConfigFactory を切り替えて、独自の ModuleConfig を生成する Factory クラスを用意すれば良いことが分かった。なるほどー、よくできてるなー。

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

  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>configFactory</param-name>
      <param-value>com.deftrash.OriginalModuleConfigFactory</param-value>      
    </init-param>
    <init-param>
      <param-name>debug</param-name>
      <param-value>2</param-value>
    </init-param>
    <init-param>
      <param-name>detail</param-name>
      <param-value>2</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

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

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

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

Posted by dT by 23:35 | Comments (0) | TrackBacks (0)

FreeBSDのJVMでエラーが起きる

FreeBSD のサーバに、Java5 と Tomcat5.5 を乗っけて起動したら、OOME で落ちてしまった。ログを見ると、かつて見たことのない内容が。

Exception in thread "CompilerThread0" java.lang.OutOfMemoryError: 
requested 1168080 bytes for Chunk::new. Out of swap space?

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

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

CATALINA_OPTS="-server"

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

サーバクラスマシンの検出にあるとおり、Java5からはサーバ環境に応じて自動でVMを切り替えてくれるということで、下手に指定なんてしなくても良かったんですね。(まあ、FreeBSDのJVMでも互換性があるのかは分かりませんが…)

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

Posted by dT by 14:05 | Comments (0) | TrackBacks (1)

ロープ・データ構造

ロープ: 理論と実践

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

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

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

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

Posted by dT by 14:15 | Comments (0) | TrackBacks (0)

tomcat6 のログ出力を Log4j で行う方法

これで、かなり快適なロギング環境を構築することができました。すごい便利。

事の始まりは、tomcat6 のログローテションを自前で行おうとしたことだった。tomcat6 では、デフォルトで日付付きのログファイルを出力するのだけど、それだと自前のローテーション処理とぶつかってしまう。これはどげんかせんといかん、と言うわけで、公式マニュアルを見たら、Log4J を使う方法が書いてあったので、「これだ!」と。

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

以下、Log4J 導入までの流れ。(ほとんどマニュアルと同じですが)

続きを読む "tomcat6 のログ出力を Log4j で行う方法"

Posted by dT by 22:25 | Comments (0) | TrackBacks (0)

JUnit4 を試してみる

今度のお仕事でようやく Java を 1.4 から 5.0 に進化させることができたので、合わせて Junit も 3.8 から 4.3 にシフト。Java5 を触り始めて、もはやアノテーション無しではコードを書けない身体になってしまったので、ここは外せない。

アノテーション 説明 JUnit3.8では
@Test テストメソッドであることを示す testXxx()
@Before 各テストメソッドの実行前に呼ばれるメソッドであることを示す setup()
@After 各テストメソッドの実行後に呼ばれるメソッドであることを示す tearDown()
@BeforeClass このテストクラスを呼び出す前に1回だけ実行するメソッドを示す
@AfterClass このテストクラスを呼び出した後に1回だけ実行するメソッドを示す
@Ignore 無視するテストメソッドであることを示す

JUnit3.8 では、テスタークラスを作るために、必死で TestCase を継承して、チマチマとコードを埋めていくということをやっていたけれども、JUnit4.3 ではアノテーションのおかげもあって、かなりラクチンに書けますね。単純なテストケースなら、public なメソッドに @Test アノテーションを付与してあげるだけで、OK。

assert の処理など細かいところは、これから見ていきまっす。でも、static import すれば、3.8 と変わらないように使えそうなので、ムダな学習コストもかからなそう。ふむふむ。

3.8 で言うところのテストスイートを書くのであれば、@SuiteClasses アノテーションを使って以下のような感じで書けばOKっぽい。

@SuiteClasses ( { Sample1Test.class, Sample2Test.class } );
public class AllSampleTests {

}

まあ、でも Eclipse3.2 以降であれば、JUnit4 に対応しているようなので、テストスイートを用意せんでも、テスト用パッケージから実行してしまえば全部できるので、テストケースに集中すればOK。というか、テストスイートの対象テストクラスを @SyuteClasses に全て羅列しなくちゃいけないのは面倒なので、テストのまとめ実行は IDE からやるのがオススメでしょうか。

お疲れさまでした。

Posted by dT by 14:50 | Comments (0) | TrackBacks (0)

厳密な日付の判定

入力された日付情報を String 型で受け取って、Date 型なり Timestamp 型に変換するというのは、よくある話。そして、入力された日付の妥当性を検証するというのも、よくある話。

そんなときは、 Calendar クラスや DateFormat クラスの setLenient メソッドを使えば、らくちんOK。

例えば年月日のフィールドが分かれているケースでは、Calendar クラスを使って次のように妥当性検証ができる。

String year = 2008;
String month = 2;
String day = 30;

try { Calendar c = Calendar.getInstance(); c.setLenient(false); c.set(Integer.parseInt(year), Integer.parseInt(month) - 1, Integer.parseInt(day)); c.getTime(); } catch (IllegalArgumentException e) { // 不正な日付の場合の処理 }

年月日を1フィールドに入力させるケースでは、DateFormat がらくちん。

String date = 2008/2/30

try { DateFormat formatter = new SimpleDateFormat("yyyy/M/d"); formatter.setLenient(false); formatter.parse(date); } catch (ParseException e) { // 不正な日付の場合の処理 }

setLenient(false) にしないと、例えば 2008/2/30 という日付でも例外をスローせず、勝手に 2008/3/1 と解釈してそのまま次の処理を行ってしまう。余計なお世話な気もする。

この話は Java の FAQ のような気がするんだけど、2日連続で方々から質問されたので、メモっときます。

Posted by dT by 14:05 | Comments (0) | TrackBacks (0)

jconsole が接続できない

リモートサーバで動いているWebアプリケーションを、jconsole で見てやろうと思ったが、どうしてか接続できない。

開発サーバということで、認証もSSLもなしという最小構成。

-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8099
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

これで接続できないとな。認証不要に設定しても、やっぱりユーザ名を指定したりするんだっけか、とか色々と試すもやっぱりムリ。困った。

結論から言うと、サーバの hosts の設定に問題があったみたい。hostname -i で 127.0.0.1 になるとダメらしい。FAQだった。

FAQ - JConsole とリモート管理

ということで、 /etc/hosts を編集してOK。

127.0.0.1	localhost.localdomain
192.168.xxx.xxx	app.localdomain

ちゃんと hostname -i で 192.168.xxx.xxx が返ってきて接続できるようになりますた。開発環境ならでは、という感じはするけど、一応メモ。

Posted by dT by 14:33 | Comments (0) | TrackBacks (0)

ジェネリクスのインスタンスを生成する方法

java でジェネリクス(総称型)のインスタンスを生成するには、どうするのか悩んでいる土曜日の昼下がり。

例えば、次のようにメソッドの総称型を定義してみる。

public  T add(T date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(Calendar.DATE, 1);
    return (T) calendar.getTime();
}

これだと、引数に java.util.Date を渡した場合は問題ないが、そのサブクラスである java.sql.Date なんかを渡すと、最後の総称型へのキャストで、ClassCastException でコケてしまう。そりゃそうだ。

同じ型のインスタンスを生成できれば、そこに値を詰めて返せるんだけど、総称型はその名のとおりクラス名を特定できないから、さて困った。

T result = new T();

みたいなことができれば良いんだけど、当然できるわけもなく。それじゃあ、と思って次に試したのがコレ。

public  T add(T date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(Calendar.DATE, 1);

T result = date.clone(); result.setTime(calendar.getTimeInMillis());
return result; }

型が分からないなら、もうクローンしちゃえば良いんじゃね、と。これは確かに動くのだけど、いやいやインスタンスを生成するのと、クローンするのでは意味が違う。

public  T add(T date) throws Exception {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.add(Calendar.DATE, 1);

T result = date.getClass().newInstance(); result.setTime(calendar.getTimeInMillis());
return result; }

ということで、今のところ、これが一番しっくり来てる。

このケースだと java.util.Date のサブクラス限定だから、このくらいで良いけど、もっと汎用化するなら、newInstance() じゃなくて Constructor とか使って生成していかないといけないんだろうなあ。でも、引数はどこから持ってくるんだ? なんてことを考えると、「やっぱクローンでも良いんじゃね?」とか思える。

どっかに良いお作法が転がってないかなあ…。

Posted by dT by 17:15 | Comments (0) | TrackBacks (0)

引数final

Java の言語仕様で、メソッドの引数にも final 修飾子を与えることができる。

public void execute(final List list) {
    //
}

これ、今までずーっと意味を勘違いしてました。「引数 object への変更は認めない」ではなくて、「引数 list への代入は認めない」という意味のようだ。なので、次のリストのようなことになる。

public void execute(final Object object) {
    list.add("aaa"); // これはOK
    list = new ArrayList(); // これはコンパイルエラー
}

つまり、引数 final は、その引数インスタンスそのものに対する意味であって、内部データを保護する意味はないのね。完全に期待していたのと違った意味付けになっていて、ホントに狼狽した。せっかくの修飾子なんだけど、ここに final を付けることの方が、余計に意味が分かりにくくなっている気がする。

ただ、唯一意味があるのは、メソッド内部で匿名クラスを使用するような場合。

public void add(Button button, final Label label) {
    button.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
            label.setText("hoge");
        }
    });
}

このようなケースでは、引数 Label に final 修飾子が付いていないと、逆にコンパイルエラーになってしまう。(メソッド内部で final 宣言した変数に入れ替えても動くので、引数 final が必須ではないけれども)

自分が言語仕様をちゃんと把握してなくてダメだって話なんだけど、またいつか別のプロジェクトではまりそうな気がするので、コーディング規約として禁止したいなあ。アノテーションもそうだけど、余計な意味付けがなされていると、深読みしちゃいますよネ。ね?

Posted by dT by 21:15 | Comments (0) | TrackBacks (0)

列挙型は継承ができない

列挙型をいくつも作成して、そこに似たようなメソッドを定義していると、「列挙型も継承できたらいいのに」と思ってしまう。が、残念ながら、列挙型は継承ができない。それも当然で、abstract な列挙型なんてありえない。仕方ないなあ。

ただ、インタフェースの実装はできるので、これを活用するのが正解。

public interface Foo {
    public int getNumber();
}

というインタフェースを定義しておいて、

public enum FooEnum implements Foo {

ONE (1), TWO (2), THREE (3);
private int number;
private FooEnum (int arg0) { this.number = arg0; }
/* (non-Javadoc) * @see Foo#getNumber() */ @Override public int getNumber() { return number; } }

という感じで、列挙型を定義してあげる。

public class Sample {
    public static void main(String[] args) {
        for (Foo foo : FooEnum.values()) {
            System.out.println(foo.getNumber());
        }
    }
}

実際にこんな感じでインタフェースプログラミングができる。

Foo インタフェースを実装した列挙型を量産する際に、いちいちコンストラクタとフィールドを用意するのが、非常にかったるいわけだけど、その列挙型ではそこを意識しなくて良くなるから、それがせめてもの救い。

ちなみに、列挙型って1.xあたりでの java 育ちな人には、あんまり積極活用されていないような気がする。文化として根付かないのかな。スゴイ便利なんだけどなあ。

Posted by dT by 00:47 | Comments (0) | TrackBacks (0)

Antで条件分岐する方法

今さらながら、Antを勉強中。

思った以上に、いろいろできるなあ。とりあえず、条件分岐できることが分かったので、開発環境と本番環境でデプロイ処理を変えるなんてことができそう。

OSで分岐するケース。Windowsで開発して、Linuxで本番稼動させるようなプロジェクトなら、こんな感じかしら。

<condition property="os" value="win">
    <os family="windows" />
</condition>
<condition property="os" value="unix">
    <os family="unix" />
</condition>

<target name="deploy"> <antcall target="${os}_deploy" /> </target>
<target name="win_deploy"> <!-- windowsの場合のデプロイ(略) --> </target>
<target name="unix_deploy"> <!-- unixの場合のデプロイ(略) --> </target>

他にも、ファイルの有無(available)やパラメータが指定の文字列を含むかどうか(contains)といった方法で判定ができる。すごい。

今まで条件に応じたターゲットを作ってきて、ターゲット名を覚えるのが面倒だったけど、この条件分岐で内部でごにょごにょしておけば、何も気にせず、ant deploy ってできるわけだ。これ、地味に良い収穫だったかも。

つーか、AntじゃなくてMavenやりたいんだけどさ。うん、まあ、オトナの事情があるんだ。そこんとこ、よろしくお願いします。

Posted by dT by 22:36 | Comments (0) | TrackBacks (0)

"Tenured Gen"と"PS Old Gen"

以前、JMXで書いたメモリ使用量の監視プログラムが、監視対象サーバを変えた途端、Exception を吐いて死亡。えええ。

JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://myserver:1234/jmxrmi");
JMXConnector conn = JMXConnectorFactory.connect(url);
MBeanServerConnection mbsc = conn.getMBeanServerConnection();

MemoryPoolMXBean mpbean = (MemoryPoolMXBean) ManagementFactory.newPlatformMXBeanProxy(mbsc, "java.lang:type=MemoryPool,name=Tenured Gen", MemoryPoolMXBean.class);

んで、エラー。

Exception in thread "main" java.lang.IllegalArgumentException: java.lang:type=MemoryPool,name=Tenured Gen not found in the connection.
at java.lang.management.ManagementFactory.newPlatformMXBeanProxy(Unknown Source)
...

「そんな ObjectName のMBean はコネクションに無いよ」とか言っちゃっているので、どんなもんがリストされているか確認。

ObjectName oName;
Set names = mbsc.queryNames(null, null);
for (Iterator it = names.iterator(); it.hasNext();) {
    objectName = (ObjectName) it.next();
    System.out.println("ObjectName: " + objectName );
}

そして、暴かれた驚愕の事実。

...
ObjectName: java.lang:type=MemoryPool,name=PS Old Gen
...

Old領域を表す名前が、「Tenured Gen」じゃなくて、「PS Old Gen」になっている。書き直してコンパイルしたら、無事に動作してくれた。ふう。

どうやらJVMが採用するGC方式によって、ここの名前が変わるみたい。JVM1.5系だと、マルチプロセッサ環境では、パラレルGC方式に自動的にスイッチされる。賢い仕様なのだけど、こんなとこにも影響が出るとは。

ということで、環境に応じて適切なプール名称を指定する方法を考えねば…。

続きを読む ""Tenured Gen"と"PS Old Gen""

Posted by dT by 14:56 | Comments (0) | TrackBacks (0)

HttpUnitでもタイムアウトを実現する

先日、「HttpUnitはタイムアウトに未対応だって」で、HttpUnitがソケット通信中のタイムアウトをサポートしていないことについて、嘆き悲しんだわけです。

もはや HttpUnit に修正パッチ当てるしか無いのかなーなんて思っていたけど、裏技っぽいものを発見。Sun JVM 限定だけど、以下のコードでタイムアウト値が設定できた。

// コネクション確立時に使用するタイムアウト値
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");

// 入力ストリーム読み出し時に使用するタイムアウト値 System.setProperty("sun.net.client.defaultReadTimeout", "10000");

プログラムの先頭で、上記のようにプロパティを設定しちゃえば、それ以降の HttpUnit が使用する URLConnection がその値を参照して動いてくれる。もしタイムアウトになった場合は、SocketTimeoutException をスローする。

ただし、プロパティを設定すると JVM 単位でしか制御できなくなるはず。サーブレットコンテナ上で使う場合や、コネクションごとに値を変えたいケースでは、現実的じゃない。簡単なクライアントだったらOKだけれども。

こうなってくると、いよいよスレッドかなー。スレッドにしておくと、後々の利便性も高そうだし、やってみる価値はありそう。

Posted by dT by 16:59 | Comments (0) | TrackBacks (0)

NekoHTML+XPathで注意すべきこと

HTMLパーサの素敵ライブラリ NekoHTML のパース結果を、XPathAPI でごにょごにょしようとしたら、上手くノードが取れない。なぜ。

Document doc = parser.getDocument();
NodeList nodelist = XPathAPI.selectNodeList(doc, "//input");

試しに Xerces2 の DOMParser を使ってみると、上手く行く。まさか相性とかじゃないし、何だろうと思ってログを眺めていたら、タグ名が大文字になって返ってきてるわけ。えー。

ちゃんとFAQに書いてありました。

Element names are written in uppercase letters (e.g., BODY). Attribute names are written in lowercase letters (e.g., lang, onsubmit).

ということで、NekoHTML のパース結果に対してタグ名処理する場合は、大文字にしなくちゃいけない。

Document doc = parser.getDocument();
NodeList nodelist = XPathAPI.selectNodeList(doc, "//INPUT");

見事にハマりました。春だなあ。

Posted by dT by 19:14 | Comments (0) | TrackBacks (0)

HttpUnitはタイムアウトに未対応だって

Webページを自動巡回するのに、HttpUnitを使ったら便利かなーと思っていたけど、コネクションのタイムアウト設定がサポートされてないっぽい。今時、JDK1.3ベースなのか。うーん、惜しい。

やっぱりcommonsのHttpClientを使うのが良いかなー。さすがに自前でソケットをイジイジすんのは辛いからなあ。

Posted by dT by 23:46 | Comments (0) | TrackBacks (0)

FindBugsにヘコまされ

遅ればせながら、FindBugsを使ってみた。

ソースレビュー代わりに使えるかと思って、今のプロジェクトで試してみたら、1600個も問題点を指摘されてしまった。最初、160個だと思って、それでもビックリしたのに、よくよく見たら1600個って。しかも自分で書いたとこも、相当数を指摘されている…。ううう、これは結構ヘコむなー。

まあ、実行前にバグが分かるということで、これは開発時に各自が使うツールなんだろうなあ。XPの代わりってことにもならないし、開発効率向上ツールの域内かしらむ。結局、ソースレビューの目的とは、ちと次元が違うんだな。期待しすぎか。

結論、やっぱり人間はスゴイ。

Posted by dT by 23:34 | Comments (0) | TrackBacks (0)

奥さん、HttpClient が便利すぎます

4年ぶりにJavaでネットワークプログラミング。

はじめは java.net.Socket か java.net.HttpURLConnection クラスで実装しようと思っていたけど、commons にて HttpClient を発見。使ってみたら、スゲー便利。そうそう、こういうのを探してました!

Jakarta Commons - HTTP Client

ダウンロードするときは、パッケージ依存している commons-logging と commons-codecs もご一緒にどうぞ。

早速トライアルということで、POSTメソッドでIDとパスワードを送ってログインするプログラムを、Socket、HttpURLConnection、HttpClient の3つのバージョンで書いてみた。

続きを読む "奥さん、HttpClient が便利すぎます"

Posted by dT by 22:31 | Comments (0) | TrackBacks (0)

Appletで半角英数字も日本語フォントにするには

JREの設定がデフォルトのまま、Appletで半角英数字と日本語の、いわゆる和欧混植をやろうとすると、英数字だけ等幅フォントになってカッコ悪い。いじめカッコ悪い。

すべて日本語フォントを使用するように変えてみようと、設定ファイルであるところの、fontconfig.properties を適当にイジってみたら上手くいったみたい。おお。

sequence.allfonts=japanese-iso10646,latin-1
sequence.allfonts.UTF-8.ja.JP=japanese-iso10646,latin-1

こんな感じで、latin-1 より先に日本語フォントが使われるように、検索順序を指定してやれば良いっぽい。よく分かってないけど。完全に自分メモです、これは。

ただでさえ慣れないJavaのフォント設定を、さらにフォント周辺が分かりづらいLinuxで行っていると、だんだん頭がおかしくなってくる気がする。ううう。

Posted by dT by 23:11 | Comments (0) | TrackBacks (0)

Tilesフレームワークは誰のため?

新規にJavaでWebアプリケーションをこさえることになったので、いろいろと方針を画策中。

画面数が多いのが気になったので、Tilesフレームワークでちょっとはラクできないかと考えたんだけど、どうも都合が悪い。画面の共通要素をタイルとして切り出すことで、必要最小限の画面生成で済むと思ったんだけど、かなり中途半端な印象。

ほとんど共通なんだけど、数箇所だけ各ページで異なるような場合がとても面倒。ムリヤリ何とかできなくも無さそうだけど、そんな保守性の下がるようなことは、今回のラクしたいという目的と背反なので、完全にボツ。JSPファイルが増えたり、設定ファイルが複雑になるのは避けたい。

そもそもTilesフレームワークというのは、簡単なレイアウト機能を提供するもので、世のWebアプリをframesetから解放するための仕組みにしか過ぎないのかな。それなら、共通要素は各JSPでimportして、レイアウトはCSSで行った方が、全然スッキリしそうだ。

個人的には、DreamWeaverのテンプレート機能のようなフレームワークが欲しいんだけど、見つけられなかった。自前で作れそうだけど、そんな時間は無いもんなあ。むむぅ。

最近、VelocityやTapestryのように、ビューとロジックを分離して、デザイナーとプログラマーの仕事を分業する仕組みばかり育っているけど、そこを同時進行する人にとっては、どうでも良い話だったりして。もしかして、Javaを選択したのがマズかったか…。

まあ、ここはスッパリとフレームワークは諦めて、ゴリゴリとJSP書いていくことにしよう。あとは運用フレームワークでカバーだなあ。うん。

Posted by dT by 23:10 | Comments (0) | TrackBacks (0)

operator does not exist: integer !=- integer

久しぶりに NetBeans で開発してみようかと思って、Tomcatを起動して動かしてみたら、こんなエラーを吐いて落ちる。なんでじゃ。APサーバー上では動いているのに。

org.postgresql.util.PSQLException: ERROR: operator does not exist: integer !=- integer

コンパイルに失敗したかなーと思って、JDKを替えて再構築するも状況は変わらず。よくよくエラー部分を見ると、SQLのパースで落ちている。とは言え、「!=-」なんて意味不明な演算子なんぞ使った覚えないんですがねー。むむむ。

んで、該当のSQLを見てみると、こんな記述。

WHERE group_id!=?

どうやら PreparedStatement でパラメータに負数が入ると落ちるということらしい。そんなことあるのか。どんなパースだ。でもAPサーバーでは動いているということは、開発環境の問題なのかな。うーむ。

続きを読む "operator does not exist: integer !=- integer"

Posted by dT by 12:59 | Comments (0) | TrackBacks (0)

jmapでオブジェクトのメモリ使用量を知る

J2SE1.5から試験導入されたトラブルシューティングツールのひとつ、jmapを使用するとヒープダンプ情報を取得することができる。どのオブジェクトが今どのくらい生成されて、どのくらいのサイズを占有しているか、定量的に見ることができる優れもの。これがJDK標準装備というのは素敵ですよ、奥さん。

とりあえず、一番わかりやすいところで、ヒープのヒストグラムを表示。

# jps
497 Jps
19719 Bootstrap

# jmap -histo 19719 > heap_histo.txt

すると、「heap_histo.txt」にメモリの現状がどわーっと表示されます。

Object Histogram:
Size        Count        Class description
-------------------------------------------------------
65156376        461687   char[]
22587104        118976   * ConstMethodKlass
19391400        484785   java.lang.String
14393048        11237    * ConstantPoolKlass
12386464        118976  * MethodKlass
10034000        102739  java.lang.Object[]

どのクラスのオブジェクトがどれくらいメモリを食っているかを知りたい場合にも良いし、メモリリーク時の原因調査の足がかりとしても使えそう。まあ、メモリリーク対策をするなら NetBeans とかのプロファイラーを使った方が良いんだけど、実運用環境で使えるという点では jmap は有用じゃないかと。

ただし、1.5ではSolaris/LinuxのJDKのみ。Windowsでは6から使えるようになるらしいッス。

Posted by dT by 23:08 | Comments (0) | TrackBacks (0)

GCViewer

GCViewer

JVMが吐き出すガーベッジコレクションのログを視覚化するツール。WindowsでもLinuxでも使える。稼働中のGCログを監視して、ダイナミックにグラフ化することもできるので、チューニング時に重宝するツールというわけです。

使い方は、いたってシンプル。JVMの起動時に、以下のようにGCログの出力先を指定して、そのログファイルを噛ませるだけ。ちなみに、これはSunのJVMの場合。HP-UX JVMとかは、-XVerbosegcとかで行けます。まあ、HP-UXだったら、HPjtuneを使った方が良いけど。

java -Xloggc:/var/log/gc.log -XX:+PrintGCDetails 

「+XX:+PrintGCDetails」を指定することで、Young領域とOld領域の情報もグラフに表示できるようになる。まあ、男は黙って「+XX:+PrintGCDetails」で良いんです。

これだけの機能があってオープンソースというのは嬉しい。

Posted by dT by 18:12 | Comments (0) | TrackBacks (0)

jstatの使い方

J2SE1.5から導入されたJVMの統計データ監視ツール、jstatの使い方メモ。

jstatを使えば、稼働中のJVMのヒープメモリの状態、パーマネント領域の状態、クラスローダーの統計情報などを参照することができる。イメージとしては、topコマンドみたいな感じで、JVMのリソース情報を監視できる。障害時の調査ユーティリティツールのひとつとして威力を発揮します。

まあ、プロファイリングツールではないので、メモリリークの有無は読み取れるけど、リーク箇所までは特定できないんで、そこは使い分けが必要だけど。

ここでは例として、Linuxのローカルマシン上で動いているtomcatの統計データを監視するケースを取り上げます。ただ、Windowsでも、同じ手順でOKのはず。

続きを読む "jstatの使い方"

Posted by dT by 12:22 | Comments (0) | TrackBacks (0)

JAVA_OPTSとCATALINA_OPTSの使い分け

tomcat起動時にJVMのオプションを指定するために、JAVA_OPTSとCATALINA_OPTSという2つの環境変数が用意されている。何が違うんだろうと思って、catalina.shを開いてみたら、両方に同じ説明が書いてある。

#   CATALINA_OPTS   (Optional) Java runtime options used when the "start",
#                   "stop", or "run" command is executed.
# 
#   JAVA_OPTS       (Optional) Java runtime options used when the "start",
#                   "stop", or "run" command is executed.

評価としては、JAVA_OPTS の次に CATALINA_OPTS がオプションとして付与されるようだけど、それがどう影響するのかわからない。オプションの順番で処理が変わったりするんだっけかな?

Posted by dT by 19:12 | Comments (0) | TrackBacks (0)

とりあえずJMeterでも使ってみるか

世間は連休ということで、しばし通常業務のことは忘れて、かねてよりマスト事項だったアプリケーションの負荷試験でもやってみようかと。とりあえず、定番のJMeterを使ってみた。

JMeterについては、事前知識はほぼゼロ。「リクエストを手軽にたくさん発行できるんでしょ?」くらいのイメージだったんだけど、これがスゴイ高機能で驚いた。

  • HTTP、FTP、LDAPをはじめ、Webサービスなど多様なリクエストを測定可能
  • アサーションによるテスト結果の評価ができる
  • 分析結果は表やツリー、グラフなど様々な形態でビジュアル化
  • レスポンスをファイルに出力できる
  • テストの実行順序や実行回数の制御が可能
  • リクエストの実行間隔を制御するタイマ機能を実装している
  • リクエスト間でのパラメータの引継ぎが可能(セッションIDとか)
  • 分散クライアントによる試験が実施できる
  • HTTPプロキシ機能でテストプランの自動生成が可能
  • ユーザー定義変数を使用したパラメータの細かい制御ができる

UIの評判が悪いようだけど、思ったより直感的に操作できた。リスナーやタイマ、処理などをツリー管理するインタフェースは、オブジェクト指向的だなあ、と思った。慣れれば問題なさそう。

ただ、ちょっと触ってみた感想としては、機能がたくさんありすぎて分からない、ということ。「ユーザーパラメータ」と「HTTPユーザーパラメータ」の違いとか、よく分からない。使いこなすためには、マニュアルの精読が必須かなあ。うむむ。

まずは一度、ちゃんとシナリオを作って、一通りをやってみるのが勉強になりそう。

続きを読む "とりあえずJMeterでも使ってみるか"

Posted by dT by 18:57 | Comments (0) | TrackBacks (0)