Javaアーカイブ

BeanUtils の使い方

かなり前に書いた記事を, ちゃんとまとめてみました.

BeanUtils の使い方サンプルです.
JUnit4 のテストケースとして書いてみました.

/*
 * $Id$ 
 */
package net.nanasess.examples;

import static org.junit.Assert.*;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.junit.Before;
import org.junit.Test;

/**
 * BeanUtils の使い方.
 * 
 * 基本的に, データ型を String に変換して返します.
 * 変換したくない場合は, {@link PropertyUtils} を使います. 
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class BeanUtilsTest {
    
    private FooBar fooBar;
    
    @Before
    public void before() {
        fooBar = new FooBar();
    }
    
    /**
     * {@link BeanUtils#cloneBean(java.lang.Object)} の使い方.
     * 
     * Cloneable を implements してなくても Bean の複製ができます.
     */
    @Test
    public final void testCloneBean() throws Exception {
        Bar bar = new Bar("bar");
        String excepted = "bar";
        
        // bar を複製する
        Bar actual = (Bar) BeanUtils.cloneBean(bar);
        
        assertEquals(excepted, actual.getExample());
    }

    /**
     * {@link BeanUtils#copyProperties(java.lang.Object, java.lang.Object)} の使い方
     * 
     * 同じプロパティ名のデータを orig から dest へコピーします.
     * この場合, example というプロパティが双方に存在するので, example のデータがコピーされます.
     */
    @Test
    public final void testCopyProperties() throws Exception {
        Bar orig = new Bar();
        ExampleClass dest = new ExampleClass();
        
        // 同じプロパティ名のデータを orig から dest へコピーする.
        BeanUtils.copyProperties(dest, orig);
        
        // orig が Map の場合は, プロパティ名と Map の key が一致した場合コピーされる.
        Map map = new HashMap();
        map.put("foo", "foo");
        BeanUtils.copyProperties(dest, map);
        
        // example の内容がコピーされる
        assertEquals(orig.getExample(), dest.getExample());
        // Map の内容もコピーされる
        assertEquals(map.get("foo"), dest.getFoo());
        // baz は null のまま 
        assertNull(dest.getBaz());
    }

    /**
     * {@link BeanUtils#copyProperty(java.lang.Object, java.lang.String, java.lang.Object)} の使い方.
     * 
     * 指定したプロパティのデータをコピーします.
     * ネストしたプロパティもコピーできます.
     */
    @Test
    public final void testCopyProperty() throws Exception {
        Bar bar = new Bar();
        ExampleClass bean = new ExampleClass();
        
        // 指定したプロパティのデータを bean にコピーします.
        BeanUtils.copyProperty(bean, "foo", "foo");
        // ネストした Bean もコピーできます.
        BeanUtils.copyProperty(bean, "bar", bar);
        
        assertEquals("foo", bean.getFoo());
        assertEquals("example", bean.getBar().getExample());
        
    }

    /**
     * {@link BeanUtils#describe(java.lang.Object)} の使い方.
     * 
     * bean のプロパティ名を key, プロパティのデータを value とした Map を返します.
     * プロパティのデータ型に関わらず, value は String になります...
     */
    @Test
    public final void testDescribe() throws Exception {
        
        // value に格納されるデータを String にキャストして Map で返します.
        Map map = BeanUtils.describe(fooBar);
        
        assertEquals("foo", map.get("foo"));
    }

    /**
     * {@link BeanUtils#getArrayProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * プロパティ名を指定して, プロパティの値を String の配列で返します.
     * プロパティのデータ型が配列の場合は, String の配列に変換して返します.
     * コレクションの場合も, String の配列に変換して返します.
     * それ以外のデータ型の場合は, {@link Object#toString()} の値を String の配列に格納して返します.
     */
    @Test
    public final void testGetArrayProperty() throws Exception {
        
        // プロパティ名を指定して, String の配列を返します.
        String[] strings = BeanUtils.getArrayProperty(fooBar, "strings");
        assertArrayEquals(fooBar.getStrings(), strings);
        
        // プロパティが配列では無い場合も String の配列が返ってきます.
        String[] foo = BeanUtils.getArrayProperty(fooBar, "foo");
        assertArrayEquals(new String[]{"foo"}, foo);
    }

    /**
     * {@link BeanUtils#getIndexedProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * プロパティ名[インデックス番号] のように, インデックスを保持するプロパティから, インデックス番号を指定して
     * 値を String で返します.
     * インデックスを保持しないプロパティは, {@link IllegalArgumentException} になります.
     */
    @Test(expected=IllegalArgumentException.class)
    public final void testGetIndexedPropertyObjectString() throws Exception {
        
        // プロパティ名とインデックス番号を指定して String の値を返します.
        String actual = BeanUtils.getIndexedProperty(fooBar, "strings[2]");
        assertEquals("ghi", actual);
        
        // インデックスを保持しないプロパティは IllegalArgumentException になります.
        String foo = BeanUtils.getIndexedProperty(fooBar, "foo[0]");
    }

    /**
     * {@link BeanUtils#getIndexedProperty(java.lang.Object, java.lang.String, int)} の使い方.
     * 
     * {@link BeanUtils#getIndexedProperty(java.lang.Object, java.lang.String)} に
     * 似ていますが, インデックス番号を int で指定するところが異なります.
     */
    @Test
    public final void testGetIndexedPropertyObjectStringInt() throws Exception {
        
        // プロパティ名とインデックス番号を指定して, String の値を取得します.
        String actual = BeanUtils.getIndexedProperty(fooBar, "strings", 2);
        assertEquals("ghi", actual);
    }

    /**
     * {@link BeanUtils#getMappedProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * プロパティが Map の場合, プロパティ名(key) のように指定して, 値を String で返します.
     * プロパティが Map 以外の場合は, null を返します.
     */
    @Test
    public final void testGetMappedPropertyObjectString() throws Exception {
        
        // プロパティ名と key を指定して, String の値を返します. 
        String actual = BeanUtils.getMappedProperty(fooBar, "map(abc)");
        assertEquals("abc of value", actual);
        
        // プロパティが Map 以外の場合は, null を返します. 
        assertNull(BeanUtils.getMappedProperty(fooBar, "foo(key)"));
    }

    /**
     * {@link BeanUtils#getMappedProperty(java.lang.Object, java.lang.String, java.lang.String)} の使い方.
     * 
     * {@link BeanUtils#getMappedProperty(java.lang.Object, java.lang.String)} に
     * 似ていますが, key を第3引数で指定するところが異なります.
     */
    @Test
    public final void testGetMappedPropertyObjectStringString() throws Exception {
        
        // プロパティ名と key を指定して, String の値を返します.
        String actual = BeanUtils.getMappedProperty(fooBar, "map", "abc");
        assertEquals("abc of value", actual);
    }

    /**
     * {@link BeanUtils#getNestedProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * ネストした Bean のプロパティをドットシンタックスで取得できます.
     * 値は String で返します.
     */
    @Test
    public final void testGetNestedProperty() throws Exception {
        
        // ネストしたプロパティを取得します.
        String actual = BeanUtils.getNestedProperty(fooBar, "bar.example");
        assertEquals("example", actual);
    }

    /**
     * {@link BeanUtils#getProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * 便利屋さんです.
     * プロパティのデータ型に合わせた方法で, プロパティ名やインデックスを指定することで, プロパティの値を
     * String で取得できます.
     */
    @Test
    public final void testGetProperty() throws Exception {
        
        // プロパティ名を指定して String の値を返します.
        String foo = BeanUtils.getProperty(fooBar, "foo");
        assertEquals("foo", foo);
        
        // プロパティ名とインデックス番号を指定して String の値を返します.
        String strings = BeanUtils.getProperty(fooBar, "strings[2]");
        assertEquals("ghi", strings);
        
        // プロパティ名と key を指定して, String の値を返します. 
        String map = BeanUtils.getProperty(fooBar, "map(abc)");
        assertEquals("abc of value", map);
        
        // ネストしたプロパティを取得します.
        String actual = BeanUtils.getProperty(fooBar, "bar.example");
        assertEquals("example", actual);
    }

    /**
     * {@link BeanUtils#getSimpleProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * プロパティ名を指定して, String の値を返します.
     * {@link BeanUtils#getProperty(java.lang.Object, java.lang.String)} とは挙動が
     * 異なるため, 単純な Bean の値を取得するのに適しています.
     */
    @Test(expected=IllegalArgumentException.class)
    public final void testGetSimpleProperty() throws Exception {
        
        // プロパティ名を指定して String の値を返します.
        String foo = BeanUtils.getSimpleProperty(fooBar, "foo");
        assertEquals("foo", foo);
        
        // プロパティが配列の場合は先頭の値を返します.
        String strings = BeanUtils.getSimpleProperty(fooBar, "strings");
        assertEquals("abc", strings);
        
        // プロパティが Map の場合はMap#toString() の値を返します. 
        String map = BeanUtils.getSimpleProperty(fooBar, "map");
        assertEquals("{abc=abc of value, ghi=ghi of value, def=def of value}", map);
        
        // ネストしたプロパティは取得できません...
        try {
            BeanUtils.getSimpleProperty(fooBar, "bar.example");
        } catch (IllegalArgumentException e) {
            throw e;
        }
    }

    /**
     * {@link BeanUtils#populate(java.lang.Object, java.util.Map)} の使い方.
     * 
     * Map に格納した値を key に対応する Bean のプロパティに設定します.
     * 値は, String にキャストして設定します.
     */
    @Test
    public final void testPopulate() throws Exception {
        Map map = new HashMap();
        map.put("foo", "foo of value");
        map.put("bar.example", new Integer(1000));
        
        // Map の key に対応するプロパティに値を設定します.
        BeanUtils.populate(fooBar, map);
        assertEquals("foo of value", fooBar.getFoo());
        
        // Integer などの値も String にキャストされます.
        assertEquals("1000", fooBar.getBar().getExample());
    }

    /**
     * {@link BeanUtils#setProperty(java.lang.Object, java.lang.String, java.lang.Object)} の使い方.
     * 
     * {@link BeanUtils#getProperty(java.lang.Object, java.lang.String)} と同様の方法で,
     * プロパティ名を指定することで, 任意のオブジェクトを String にキャストして Bean に設定します.
     */
    @Test
    public final void testSetProperty() throws Exception {
        
        // プロパティ名を指定して String の値を設定します.
        BeanUtils.setProperty(fooBar, "foo", "foo of value");
        assertEquals("foo of value", fooBar.getFoo());
        
        // プロパティ名とインデックス番号を指定して String の値を設定します.
        BeanUtils.setProperty(fooBar, "strings[2]", "strings of two");
        String[] strings = fooBar.getStrings();
        assertEquals("strings of two", strings[2]);
        
        // プロパティ名と key を指定して, String の値を設定します. 
        BeanUtils.setProperty(fooBar, "map(abc)", new Integer(1000));
        assertEquals("1000", fooBar.getMap().get("abc"));
        
        // ネストしたプロパティを設定します.
        BeanUtils.setProperty(fooBar, "bar.example", "bar of example");
        assertEquals("bar of example", fooBar.getBar().getExample());
    }
}

Bean は下記のものを使っています.

FooBar.java
/*
 * $Id$ 
 */
package net.nanasess.examples;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * FooBar クラス.
 * 
 * 内部で Bar クラスをネストしている.
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class FooBar {

    private String foo;
    private Bar bar;
    private String[] strings;
    private List bars;
    private Map map;
    
    public FooBar() {
        this.foo = "foo";
        bar = new Bar();
        strings = new String[]{"abc", "def", "ghi"};
        bars = new ArrayList();
        map = new HashMap();
        for (String string : strings) {
            bars.add(new Bar(string));
            map.put(string, string + " of value");
        }
    }
    
    public String getFoo() {
        return foo;
    }
    public void setFoo(String foo) {
        this.foo = foo;
    }
    public Bar getBar() {
        return bar;
    }
    public void setBar(Bar bar) {
        this.bar = bar;
    }
    public String[] getStrings() {
        return strings;
    }
    public void setStrings(String[] strings) {
        this.strings = strings;
    }
    public List getBars() {
        return bars;
    }
    public void setBars(List bars) {
        this.bars = bars;
    }
    public Map getMap() {
        return map;
    }
    public void setMap(Map map) {
        this.map = map;
    }
}

Bar.java
/*
 * $Id$ 
 */
package net.nanasess.examples;

import java.sql.Timestamp;

/**
 * Bar クラス.
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class Bar {

    private String example;
    private Timestamp created;

    public Bar() {
        this.example = "example";
        this.created = new Timestamp(System.currentTimeMillis());
    }
    
    public Bar(String example) {
        this.example = example;
        this.created = new Timestamp(System.currentTimeMillis());
    }
    
    public String getExample() {
        return example;
    }

    public void setExample(String example) {
        this.example = example;
    }

    public Timestamp getCreated() {
        return created;
    }

    public void setCreated(Timestamp created) {
        this.created = created;
    }
}

ExampleClass.java
/*
 * $Id$ 
 */
package net.nanasess.examples;

import java.sql.Timestamp;

/**
 * ExampleClass クラス.
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class ExampleClass {
    
    private String foo;
    private String baz;
    private String example;
    private Bar bar;
    private Timestamp created;

    public String getFoo() {
        return foo;
    }

    public void setFoo(String foo) {
        this.foo = foo;
    }

    public String getBaz() {
        return baz;
    }

    public void setBaz(String baz) {
        this.baz = baz;
    }
    
    public String getExample() {
        return example;
    }
    
    public void setExample(String example) {
        this.example = example;
    }

    public Bar getBar() {
        return bar;
    }

    public void setBar(Bar bar) {
        this.bar = bar;
    }

    public Timestamp getCreated() {
        return created;
    }

    public void setCreated(Timestamp created) {
        this.created = created;
    }
}

以前の記事にも書いたのですが, BeanUtils は, 基本的に値を String にキャストします.
PropertyUtils は, 型変換せずに扱います.
それ以外の振舞いは, 基本的に同じです.

これらの API を使いこなすと非常に強力ですが, 最近はフレームワークがサポートしている場合が多いようです.
(Seasar の S2BeanUtils など)

今回ひさびさに, BeanUtils のソースを読んだんですが, やはりリフレクションの良いお手本です.

PropertyUtils のサンプルコードは, また後日...

JUnit4 ことはじめ

ずっと食わず嫌いだった JUnit4 を使ってみたので, コードを晒しておきます...

/*
 * $Id$ 
 */
package net.nanasess.examples;

import java.util.ArrayList;
import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * HelloWorld のテスト.
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class HelloWorldTest {

    private static String expected;
    private String actual;
    private static int counter;    

    /**
     * すべてのテスト前に実行するメソッド.
     * 
     * static にして {@link @beforeClass} アノテーションをつける.
     */
    @BeforeClass
    public static void beforeClass() {
        System.out.println("HelloWorld テストを始めます.");
        counter = 0;
        System.out.println("カウンターを初期化しました.");
        
    }
    
    /**
     * すべてのテストを終了した後に実行するメソッド.
     * 
     * static にして {@link @AfterClass} アノテーションをつける.
     */
    @AfterClass
    public static void afterClass() {
        System.out.println(counter + " 個のテストを実行しました.");
        System.out.println("HelloWorld テストを終了します.");
    }
    
    /**
     * 各テストをする前に実行するメソッド.
     * 
     * {@link @Before} アノテーションをつける.
     */
    @Before
    public void before(){
        expected = "HelloWorld";
        System.out.println("expected を初期化しました.");
    }
    
    /**
     * 各テストの後に実行するメソッド.
     * 
     * {@link @After} アノテーションをつける. 
     */
    @After
    public void after() {
        counter++;
        System.out.println(String.valueOf(counter) + " 番目のテストを実行しました.");
    }

    /**
     * HelloWorld のテスト.
     */
    @Test
    public void helloWorld() {
        actual = "HelloWorld";
        assertEquals(expected, actual);
    }

    /**
     * ちゃんと ClassNotFoundException になるかどうかのテスト.
     * 
     * @throws Exception エラーが発生した場合.
     */
    @Test(expected = ClassNotFoundException.class)
    public void classForNameFailure() throws Exception {
        Class.forName("org.postgresql.Driver");
    }

    /**
     * スタブなので, スキップするテスト.
     * 
     * {@link @Ignore} アノテーションをつけると, そのメソッドはテストしない.
     */
    @Ignore("スタブなのでテストしません...")
    @Test
    public void testStub() {
        assertEquals("test", "stub");
    }

    /**
     * 配列を比較するテスト.
     * 
     * {@link org.junit.Assert#assertArrayEquals(String[], String[])} を使ってます.
     * 配列や, List のテストも楽になったものです(しみじみ)
     */
    @Test
    public void arrays() {
        String[] expecteds = { "1", "2", "3" };
        List actuals = new ArrayList();
        actuals.add("1");
        actuals.add("2");
        actuals.add("3");

        assertArrayEquals(expecteds, actuals.toArray(new String[actuals.size()]));
    }
}

個人的には, 配列や例外のテストが解りやすく楽になったのが良い感じ.
JUnit4 の関連ライブラリも充実してきたので, そろそろ乗り替え時かな...

ついでに, Eclipse 3.4M6a を Celeron 600MHz, メモリ 384MB という非力なノートPCで使ってみましたが, 思ったよりサクサクで良い感じでした☆

JIS X 0213:2004(JIS2004) と Java と PHP と RDBMS

ずいぶん前に, EC-CUBE の ML へ投稿した話題ですが, どうも過去ログに埋もれているので, 再度まとめてみます...

Windows Vista で, 日本語の文字集合に, JIS X 0213:2004(JIS2004) が採用されました.
このため, 扱える漢字が増えたり, 字体が変化したりして, さまざまな影響があります.

EC-CUBE を代表する PHP のアプリケーションや, Java の場合, どのような影響が考えられるでしょうか.

以前, Windows Vista での影響調査をする機会があり, いろいろ調べましたので, ちょっとまとめてみます.

文字集合と文字エンコーディングについて

文字のまとまりを規格化できるよう, 各文字に対して一連のコード(文字コード)を割り当てたものを「文字集合(文字セット)」と言います.
これには, Unicode や, JISX 0213:2004 などがあたります.

この文字集合をコンピュータで扱えるよう, 文字コードをバイトコードへ変換する仕組み(文字符号化方式)を文字エンコーディングと言います.
これには, Shift_JIS や, EUC-JP, UTF-8, UTF-16 などがあります.

文字コードと文字エンコーディングと文字セットを混同してしまいがちですが, それぞれの意味は別物です.

UTF-8 というのは, 正確に言うと Unicode という文字セットで規定されている文字エンコーディングの一つです.

Unicode で規定されている文字エンコーディングの抜粋

  • UTF-1 - 8bit
  • UTF-5 - 利用されていない
  • UTF-7 - UTF-16 で表した Unicode を Base64 で変換して表す方式. 非推奨
  • UTF-8 - 8bit 単位の可変長コードにエンコードする方式
    • 1バイト - すべての ASCII
    • 2バイト - JIS X 0208の非漢字の一部
    • 3バイト - Windows-31J の拡張文字エリア全て, 第3・4水準漢字の一部
    • 4バイト - JIS X 0213:2004 の第3・4水準漢字の一部, UnicodeのBMP 面以外すべて
  • UTF-16 - Windows XP, Vista の内部エンコーディング. Vista では, JIS X 0213:2004 の文字集合が採用された. 16bit 単位の可変長. 16bit のみで表現される BMP面と, 16bit + 16bit で表現される「サロゲート・ペア」から構成される.
  • UTF-32 - Unicode の全コードを単一長のコードとして 32bit で指定するコード. 実際に使用されるのは 21bit までの範囲.

Unicode における BMP(Basic Multilingual Plane)面とは, UTF-16 にて, 16bit のみで表現できる領域の文字のことを言い, 16bit + 16bit で表現する文字の範囲を 「サロゲート・ペア」と言います.

Unicode 以外の文字集合としては, UCS-4 (ISO/IEC 1064)という良く似た別物があり, UCS-4 で規定されている文字エンコーディングにも UTF-8 があったりします.

UCS-4 と Unicode は 21bit までの範囲で互換性があります. UCS-4 は4バイトを基本とし, 31bit までの領域を使用します. ややこしい...

一方, JIS X 0213:2004 では, 文字エンコーディングとして, ISO-2202-JP-2004, EUC-JP-2004, Shift_JIS-2004 などがありますが, IANA に登録されていないため, あくまでも参考となっています.

Windows Vista での問題

Windows Vista では, 使用する文字セットに, JIS X 0213:2004 を採用したため, UTF-16 でエンコーディングされる場合, BMP面以外の文字(16bit + 16bit で表現される文字)までサポートされることとなりました.

このため, UTF-8 でエンコーディングされる場合は, 1文字が4バイトで表現される文字も含まれます.

PHPでの扱い

http://jp.php.net/mbstring を見るかぎりは, mbstring 関数を使用することで, サロゲート・ペアの文字が入力されても, うまく対応できそうです.
文字エンコーディングは, UTF-8 が無難でしょう.

MySQL での問題

http://www.mysql.gr.jp/mysqlml/mysql/msg/13823
これを見ると, DBエンコーディングに UTF-8 を使用した場合でも, 扱えるのは, 1文字3バイトまでの範囲に限られるようです...

つまり, Windows Vista で採用されたサロゲート・ペアの文字が入力された場合は, 確実に文字化けします(苦笑)

PostgreSQL での扱い

8.1.7以降、8.2.2以降であれば, UTF-8 で問題なく扱えるようです.
それ以前のバージョンは, 1文字4バイトで表現される文字を使用した場合に問題がでる可能性あります.

http://mlog.euqset.org/archives/pgsql-jp.ml.postgresql.jp/38404.html

Oracle は?

Oracle は 10.1.0以降で対応とのことです.

Java での問題

char が 16bit であるため, 16bit + 16bit のサロゲート・ペアは正常に扱えません...

具体的には, String#length() や, String#substring() が正常に動作しません(苦笑)

プロパティファイルなど, \uXXXX で表わすものは 16bit なので, サロゲート・ペアの文字は使えません.
プロパティファイルを XML で書けば, 任意のエンコーディングを使用できるので, サロゲート・ペアの文字も使用できます.

Java でサロゲート・ペアの文字列を扱うには, Java SE 5.0 以降で追加された API を使います.

サロゲート・ペアが含まれる文字数を正確に数えるには, String#codePointCount() を使ったりします.

文字列のソートとかも工夫が必要です. 手前味噌ですが, 下記にサンプルがあります.
http://d.hatena.ne.jp/nanasess/20070819/1187700166

さらに...

http://itpro.nikkeibp.co.jp/99/vista/index.html
これを見ると, Vista で入力できても, XP 以前では対応していない文字が183字あり, この中には人名漢字も含まれます.

仮にWebアプリケーション側が Vista にフル対応したとしても, XP では表示できないというトラブルも発生します.

XP に JIS2004対応フォントをインストールしたりすることで回避できますが, 対応をクライアント側に任せるのもどうかと...

いちばん良いのは, Vista で問題の文字を入力した場合に警告を出すことでしょうかねえ...

参考

J2SE 5.0 Tiger 虎の穴 Unicode 4.0 の補助文字のサポート

Java プラットフォームにおける補助文字のサポート

新しいプログラミング言語を習得するための15の方法

新しいプログラミング言語を習得するための15の方法
http://d.hatena.ne.jp/kajidai/20061025/1161711473

hanoimania!
http://www.kernelthread.com/hanoi/

で、紹介されている言語すべてで、この15の方法が書けるようになったらすごいだろうなーと。
sendmail.cf で書くとかはさすがに無理かもしれないけれど、少しずつでもいいからやってみようかなと思ったりする(笑)

続かなかったらスミマセン。
まずは Java からかなぁ。

Subversion でモジュールを管理する

Java に限らず、アプリケーションを開発する場合において、いくつかのアプリケーションで共通的に使用するモジュールを作成すると思います。

Subversion の svn:externals 属性を使用すると、これらの共通モジュールを管理するのが非常に楽になります。
ディレクトリ単位でしか使用できませんが、かなり重宝します。

使い方は簡単。
svn:externals という属性をディレクトリに対してセットしてやるだけで、そこで指定したリポジトリのディレクトリを update した時にコピーしてくれます。

詳しくは下記参照
Subversion - 外部定義

Eclipse から GWT を起動する for Mac

GWT が Mac OS X にネイティブ対応 したので、早速導入。
基本的には projectCreator で Eclipse のプロジェクトを作って、 Terminal から applicationCreator でデバックしながら開発という感じになるんでしょうけど、これだと Eclipse の デバックモードが使えず、ちょっと不便。
ということで、 Eclipse から起動させる方法を模索。
最初は、googlipse というプラグインを使ってみたのだけど、--XstartOnFirstThread の引数がないと怒られる。
あいにく、googlipse は、 任意の引数を与えることができないらしい。

試行錯誤のあげく、起動構成から直接 GWTShell をたたくことで成功。
具体的には、

1. 実行→構成および実行を選択。
2. メインタブで任意のプロジェクト(GWTTest)を選択、メインクラスは com.google.gwt.dev.GWTShell を指定
3. 引数のタブで、 プログラムの引数に下記を追加

 -port 8888 -out "www"   jp.example.GWTTest/GWTTest.html

4. VM引数に下記を追加
-XstartOnFirstThread

5. クラスパスのタブで、拡張をクリック、プロジェクトのディレクトリである src と bin をクラスパスに追加

これで実行をクリックすると、 Eclipse から GWT の hosted mode が立ち上がります!

さあ、ばりばりやるぞー

Google Web Toolkit (GWT) を Mac OS X で開発する

記事は少し前に見つけていたのですが、なかなか時間が無く、やっと試すことができたのでまとめてみます。

Java で Ajax の開発ができる!という Google Web Toolkit (GWT)が大変注目を集めています。
が、開発環境としてサポートしているのが、Linux (GTK+ 2.2.1+) と、 Windows XP/2000 のみということなので、 Mac OS X ユーザーは非常につらいところ。

この開発環境を何とか Mac OS X で動かすことができないかと, いろいろ調べてみたところ, Google Code に Issue #7480804 - Mac OS X support for Google Web Toolkit というのを発見.

そこに, Using GWT on Mac OS X: How To Guide という Google Group の記事があったので試してみました.

  • まず, http://www.eclipse.org/swt/ から, Mac OS X の SWT をダウンロードし, ‾/Library/Java/swt 等へ展開.
  • Linux 版の GWT をダウンロードし展開.
  • GWT を展開したディレクトリへ移動し, 下記のコマンドを実行. GWT プロジェクトを作成.
    % ./projectCreator -eclipse GWTTest -out ‾/Documents/workspace/GWTTest
    

    こうすることにより, Eclipse のプロジェクトが作成されます. プロジェクト名と, workspace のパスは, 環境に応じて書き変えてください.

  • 次に, 下記コマンドで必要なファイルを生成.

    % ./applicationCreator -eclipse GWTTest -out ‾/Documents/workspace/GWTTest jp.example.gwttest.client.GWTExample
    

    GWT アプリケーションは, 必ず client パッケージを指定する必要があるようです.

  • 生成したプロジェクトのディレクトリへ移動すると, GWTExample-shell や GWTExample-compile といった shell スクリプトが生成されていると思うので, GWTExample-shell を下記のように編集.

    #!/bin/bash
    
    

    APPDIR=`dirname $0`;
    java ¥
    -XstartOnFirstThread ¥
    -cp "$APPDIR/src:$APPDIR/bin:/foo/bar/gwt-linux-1.1.10/gwt-user.jar:/foo/bar/swt/swt.jar:/foo/bar/gwt-linux-1.1.10/gwt-dev-linux.jar" ¥
    -Djava.library.path=/foo/bar/swt ¥
    com.google.gwt.dev.GWTShell -out "$APPDIR/www" "$@" jp.example.gwttest.GWTExample/GWTExample.html;

    具体的には, 次のオプションを追加.
    -Djava.library.path には, SWT を展開したパスを指定して下さい.

    -XstartOnFirstThread
    -Djava.library.path=/foo/bar/swt
    

    クラスパスの gwt-dev-linux.jar の前に, swt.jar のクラスパスを追加しました.

      編集した GWTExample-shell を Terminal 等から実行すると, GWT アプリケーションがホステッドモードで立ちあがります.

%E3%83%92%E3%82%9A%E3%82%AF%E3%83%81%E3%83%A3%201.png

しかし, GWTExample-compile でWebモードへコンパイルしないと, GWTアプリケーションが動作しないようです..(苦笑)
Compile/Browse ボタンも動作しないようです..

つまり, JavaScript へコンパイルしないと動作しない模様..
ホステッドモードで debug 可能なのが GWT の良さでもあるので, 魅力半減..

でも, いちいちブラウザを立ちあげて確認するよりも, 少しは手間が省けるので, Mac OS X が正式にサポートされるまでの繋ぎとして使うのにはこれで我慢ですね..

trac

2ヶ月ほど前から、プロジェクト管理を GForge から trac に乗り換えました。
Wiki ベースのプロジェクト管理ツールで、Subversion との連携は抜群。バグトラッカであるチケットシステムもシンプルでなじみやすいです。

乗り換えた最大の理由は、 trac が Python で書かれていること。
GForge は PHP4 で書かれており、GForge を使うためだけに サーバーに mod_php4 を入れていました。
apache2.0.x を使っているので、mod_php4 の使用は推奨されてないんですよね。。
また、最近は PHP で開発することもほとんど無くなり。。。必要性がかなり薄れておりました。
とりあえず、すっきり PHP とオサラバしたかったのです(笑)

最近は、 trac で開発を行っているプロジェクトもよく見られるようになってきました。国内では Meadow とか、 Namazu など。
WWDC 2006で存在明らかにされたMac OS Xの次バージョン「Leopard」に搭載予定のカレンダーサーバーである、 Darwin Calendar Server も trac で開発が行われるようです。

日本語の情報が、まだまだ少ないようなので、時間があったら少しずつでも、ノウハウまとめていきたいと思っておりますが。。。
できるのか?(汗)

Java で Ajax が書ける!

Google Web Toolkit

これすごい。
Java 使いにとっては朗報。
いまのところ、実行環境が Windows と Linux のみのサポートだそうで、 Mac 使いにはちょっとツライ。
でも、しつこく試してみたところ、実行環境は動かないけど、 Java のソースコードから、 HTML と JavaScript のコードは生成出来る模様。
このあたりは Java で動いているみたいです。
もちろん、それをブラウザで開けばちゃんと動きます。
試してないけど、 Debugging in Hosted Mode なんてものもあるので、 Eclipse 使えば動くのかな?

使い方覚えれば、かなり使えるかなと。
いま使っている、 Struts ベースのフレームワークと、どう連携させようか・・・。
落ち着いたらまた書きます(汗)

基本。

フォームに入力すると、見積書とか、請求書が PDF で自動的にできるソフト作ってくれーとの依頼入る。

Webベースにして、Java で作ることに。
Webベースにすることで、PCにインストールする手間が無くなり、ブラウザなら誰でも簡単に扱えるという利点がある。
ていうか、以前作ったことあるので、ちょっとカスタマイズするだけでできるのだが、くそまじめに作ろうと思い立ち、設計書から作ることに。
大規模なプロジェクトになると、概要設計→基本設計→詳細設計→製造→結合テスト→Quality Assurance(品質保証テスト) という流れは必須なのだが、小規模なアプリなら、ざっとER図とかクラス図を書いたりして、簡単に設計して、コーディングというように省略してしまう。

しかし、今回は、ちょっと基本をみっちりやろうと、概要設計から作っております。
設計書は、Excel、Word、PowerPoint等で作ることが多いと思いますが、僕はテキストベースで、Subversion 使って管理したいのです。
テキストベースにすることによって、プラットホームやバージョンの依存性が無くなり、Excel が入ってないからみれないとか、長い年月が経ち、作ったソフトが廃止され、ファイルが開けないということもなくなります。

いまやってるプロジェクトでは、APT(Almost Plain Text)というのを使っており、

Aptconvert is a command-line tool that can be used to convert the APT format to HTML, XHTML, PDF, PostScript, (MS Word loadable) RTF, DocBook SGML and DocBook XML.

ということで、かなりいろいろなフォーマットへ変換でき、大変優秀であります。
ベースとなるテキストファイルも Wiki に似た文法で、そのままでも読みやすい。
しかし欠点がただ一つ。
表が非常に書きにくいこと。
*+-:|を駆使して、表を書かねばならんのです。


*----------+--------:
|こんな | 感じ |
*----------+--------:

これ、超面倒(ーー;

かといって、LaTeX で書くほどのものでもないし・・・。

なんか良いツールは無いかと探していたところ、 SmartDoc はっけん。
前から知っていたのだけど、業界標準と言うほど使われておらず、デザインの柔軟性がちょっと乏しいかなと思い、敬遠しておりました。
また、僕は XHTML で出力したいので、これがないのがネックでした。
でも、それは、まだ SmartDoc が0.6とかのバージョンだった頃のお話。

現在、1.2b となって、かなり使えるツールになっております。
-html4.xhtml:true というオプションをつけると、XHTML で出力できるし、オプションと CSS を駆使すれば、かなり柔軟に HTML出力可能。
また、 PureSmartDoc 形式で書き出せば、XML DB と連携したり、 Java と連携したりと汎用性抜群。

ということで、いま、 SmartDoc を使ってごそごそ基本設計書を作っております。

で、いま作ってる帳票アプリは、機能が安定してきたらオープンソースで公開しても良いかなぁと思ってます。
PDFに出力する部分は、 JasperReports 使います。
こういうのは、一度作ってしまえば、割と楽なんですが、最初はなかなか敷居が高いですからねえ。。

とりあえず、基本に返って地道に頑張ります。。