ひよっこ。

I want to…

Posts Tagged ‘java’

JIRAのプラグインマネージャーをプロキシ経由で有効にする

Posted by hikaruworld : 2012 4月 14

JIRAというよりGreenHooperを試してみたくて、
ちょっとJIRAインストールしていました。

インストール自体は簡単だったんですが、
色々めんどくさいですね試用するだけでも。
ダウンロード先まで迷ってしまいました。

で、本題。

GreenHooperのインストールはJIRAの管理メニューから
プラグインマネージャーというものを利用して行います。

ただし、このプラグインマネージャー、
自分の環境だとproxy経由で接続しており、そのせいでプラグインサイトに接続できません。
プロキシ設定くらいならJIRA管理メニューで設定できんじゃね?と思ったら見つかりません。
ドキュメントに書いてあるかな?と思ったんですが見当たりません。

困ったなーと思いつつ、
JavaでTomcatだしなと思ってcatalina.shでJAVA_OPTSでプロキシ設定をしてみました。
こんな感じの設定をcatalina.shの冒頭に設定します。

JAVA_OPTS="$JAVA_OPTS -Dhttp.proxyHost=ホスト名 -Dhttp.proxyPort=ポート番号 -Dhttp.nonProxyHosts=プロキシを通さないURL"

そうすると無事プラグインマネージャーからGreenHooperをインストールできました。
多分ちゃんとGUIのどこかで設定できるとおもうんですけども….
以上です。

Posted in program | タグ: , , | Leave a Comment »

DBUnitでテストを書いてたらPotential problem found(再修正)

Posted by hikaruworld : 2011 9月 28

DBUnitでテストを書いてたらPotential problem foundの修正が上手くいったと
勘違いしてしまったので再度修正。
ここによると、毎回インスタンスが生成されるのでOverrideしろとのこと。

というわけで、再度修正。

    import org.dbunit.database.IDatabaseConnection;
    import org.dbunit.DatabaseUnitException;
    import java.sql.Connection;
    import org.dbunit.database.DatabaseConnection;
    import org.dbunit.database.DatabaseConfig;
    import org.dbunit.ext.postgresql.PostgresqlDataTypeFactory;

    //....

    /**
     * Connection情報を取得する
     * @return 取得されたDatabaseConnection情報
     * @throws ClassNotFoundException {@link ClassNotFoundException}
     * @throws SQLException {@link SQLException}
     * @throws DatabaseUnitException {@link DatabaseUnitException}
     */
    private IDatabaseConnection getConnection() throws DatabaseUnitException {
    	 // SpringFrameworkでsqlSessionFactoryを注入済み
        Connection conn = this.sqlSessionFactory.openSession().getConnection();
        return new DatabaseConnection(conn, this.schemaName) {
            @Override
            public DatabaseConfig getConfig() {
                DatabaseConfig config =  super.getConfig();
                config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new PostgresqlDataTypeFactory());

                return config;
            }
        };
    }

Posted in program | タグ: , , , | Leave a Comment »

DBUnitでテストを書いてたらPotential problem found

Posted by hikaruworld : 2011 6月 2

やっぱりうまくいかなかったので、こちらに追記しています。

発生したのはこんなエラー。

WARN : org.dbunit.dataset.AbstractTableMetaData – Potential problem found: The configured data type factory ‘class org.dbunit.dataset.datatype.DefaultDataTypeFactory’ might cause problems with the current database ‘PostgreSQL’ (e.g. some datatypes may not be supported properly). In rare cases you might see this message because the list of supported database products is incomplete (list=[derby]). If so please request a java-class update via the forums.If you are using your own IDataTypeFactory extending DefaultDataTypeFactory, ensure that you override getValidDbProducts() to specify the supported database products. L166

なんじゃらほ、と思って調べていると、DBUnitのFAQでこんなことが書いてあることを発見。

というわけで、DatabaseConnectionから取得出来るDatabaseConfigに以下の設定を追加して解決。

DatabaseConfig config = databaseConnection.getConfig();
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new PostgresqlDataTypeFactory());

以上です。

Posted in program | タグ: , , , | Leave a Comment »

Javaの正規表現で日本語のみを許す場合を考えてた

Posted by hikaruworld : 2011 1月 29

とりあえず、Java1.6でUTF-8の場合に、
俗にいう日本語(笑)?と英数だけ通したいなーと思って正規表現書いてた。
日本語とか空白とか、句読点とか、空文字だけは駄目らしい。。
こんな感じでいいのか、かなり不安。

あとでテスト書こう…。

そういやCJKって中国語、日本語、韓国語の頭文字だったんだね。
知らんかった。。。

/**  俗にいう日本語???と半角英数のみを許可するPattern */
public static final String IN_JP_PATTERN = "^"	// 行の先頭
  // 空文字("")を許可
  + "\\??|"
  // Unicodeブロックでひらがな
  // -> http://code.cside.com/3rdpage/jp/utf-8/Hiragana.html
  + "[\\p{InHiragana}|"
  // Unicodeブロックでカタカナ
  // -> http://code.cside.com/3rdpage/jp/utf-8/Katakana.html
  + "\\p{InKatakana}|"
  // Unicodeブロックで半角カタカナ
  // -> ?
  + "\\p{InHalfwidthAndFullwidthForms}|"
  // CJK互換漢字
  // http://code.cside.com/3rdpage/jp/utf-8/CJK_Compatibility_Ideographs.html
  + "\\p{InCJKUnifiedIdeographs}|"
  // CJK互換用文字
  // http://code.cside.com/3rdpage/jp/utf-8/CJK_Compatibility.html
  //+ "\\p{InCJKCompatibility}|"
  // CJK互換形
  // http://code.cside.com/3rdpage/jp/utf-8/CJK_Compatibility_Forms.html
  // + "\\p{InCJKCompatibilityForms}|"
  // CJK互換漢字
  // http://code.cside.com/3rdpage/jp/utf-8/CJK_Compatibility_Ideographs.html
  // + "\\p{InCJKCompatibilityIdeographs}|"
  // CJK互換漢字補助
  // http://code.cside.com/3rdpage/jp/utf-8/CJK_Compatibility_Ideographs_Supplement.html
  // + "\\p{InCJKCompatibilityIdeographsSupplement}|"
  // CJK部首補助
  // http://code.cside.com/3rdpage/jp/utf-8/CJK_Radicals_Supplement.html
  // + "\\p{InCJKRadicalsSupplement}|"
  // CJKの記号及び句読点
  // http://code.cside.com/3rdpage/jp/utf-8/CJK_Symbols_and_Punctuation.html
  // + "\\p{InCJKSymbolsAndPunctuation}|"
  // CJK統合漢字拡張A http://code.cside.com/3rdpage/jp/utf-8/CJK_Unified_Ideographs_Extension_A.html
  // + "\\p{InCJKUnifiedIdeographsExtensionA}|"
  // CJK統合漢字拡張B http://code.cside.com/3rdpage/jp/utf-8/CJK_Unified_Ideographs_Extension_B.html
  // + "\\p{InCJKUnifiedIdeographsExtensionB}|"
  // 囲みCJK文字
  // 月http://code.cside.com/3rdpage/jp/utf-8/Enclosed_CJK_Letters_and_Months.html
  // + "\\p{InEnclosedCJKLettersAndMonths}|"
  + "a-zA-Z|"// a ~ z または A ~ Z
  + "\\d"// 数字
  + "]"
  + "+$";// 行の末尾

参考:東アジア文字:ユニコード文字一覧表
参考:日本語に絡むUnicodeブロックとスクリプト(正規表現)
参考:漢 字 文 献 情 報 処 理 研 究 会 メ ー ル マ ガ ジ ン

Posted in program | タグ: , | Leave a Comment »

hamcrestで年月日比較がしたくなったのでDateMatcherを作ってみた

Posted by hikaruworld : 2010 10月 21

とりあえず、車輪の再発明な気がします。
どっかにきっとあると思うんだ。

以下、本題です。

hamcrestパッケージの叙述的?記法は結構気に入ってるのですが、
Date系のMatcherがあんまりなくて、今のテストだと、
年だけとか、月だけとか、日だけの検証がしたくなってきました。

hamcrestのMatcherクラスは結構簡単に拡張出来るので書いてみました。
以下のメソッドを利用して比較出来ます

// 年・月・日が正しいことを比較
assertThat(Date, isDate(Date));
// 年が正しいことを比較
assertThat(Date, isYear(int));
// 月が正しいことを比較
assertThat(Date, isMonth(int));
// 日が正しいことを比較
assertThat(Date, isDay(int));

以下ソースです。

import java.util.Calendar;
import java.util.Date;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
/**
 * Date比較用のMatcherクラス.
 * 年、月、日、あるいは年月日を対象に値の検証を行う
 * @author morikawa
 */
public class DateMatcher extends BaseMatcher<Date> {

    /** Matcher対象のオブジェクト */
    private Date entity;
    /** 出力メッセージ */
    private StringBuilder message;
    /** 検証対象フィールド */
    private DateField field;
    /** 比較フィールドの列挙型 **/    
    public static enum DateField {
        /** インスタンス自体の比較 */
        ALL {
            @Override
            public boolean is(Date base, Date target) {
                return YEAR.is(base, target) && MONTH.is(base, target) && DAY.is(base, target);
            }

            @Override
            public void addDescription(Description description, Date target) {
                description.appendValue(target);
            }
        },
        /** 年比較 */
        YEAR {
            @Override
            public boolean is(Date base, Date target) {
                Calendar baseCal = Calendar.getInstance();
                Calendar targetCal = Calendar.getInstance();
                baseCal.setTime(base);
                targetCal.setTime(target);

                return baseCal.get(Calendar.YEAR) == targetCal.get(Calendar.YEAR);
            }

            @Override
            public void addDescription(Description description, Date target) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(target);
                description.appendValue("YEAR::" + calendar.get(Calendar.YEAR));
            }
        },
        /** 月比較 */
        MONTH {
            @Override
            public boolean is(Date base, Date target) {
                Calendar baseCal = Calendar.getInstance();
                Calendar targetCal = Calendar.getInstance();
                baseCal.setTime(base);
                targetCal.setTime(target);

                return baseCal.get(Calendar.MONTH) == targetCal.get(Calendar.MONTH);
            }

            @Override
            public void addDescription(Description description, Date target) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(target);
                description.appendValue("MONTH::" + calendar.get(Calendar.MONTH));
            }
        },
        /** 日比較 */
        DAY {
            @Override
            public boolean is(Date base, Date target) {
                Calendar baseCal = Calendar.getInstance();
                Calendar targetCal = Calendar.getInstance();
                baseCal.setTime(base);
                targetCal.setTime(target);

                return baseCal.get(Calendar.DAY_OF_MONTH) == targetCal.get(Calendar.DAY_OF_MONTH);
            }

            @Override
            public void addDescription(Description description, Date target) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(target);
                description.appendValue("DAY_OF_MONTH::" + calendar.get(Calendar.DAY_OF_MONTH));
            }
        };
        /**
         * 値が同じであることの比較を行う。
         * @param base 比較元のDate
         * @param target 比較対象のDate
         * @return 値が同一の場合にtrue
         */
        abstract boolean is(Date base, Date target);
        /**
         * Matcherに検証対象オブジェクトの情報を設定する
         * @param description 概要
         * @param date 対象のDate
         */
        abstract void addDescription(Description description, Date target);
    }
    
    public DateMatcher(Date date, DateField field) {
        this.entity =  date;
        if (field == null) {
            this.field = DateField.ALL;
        } else {
            this.field = field;
        }
        this.message = new StringBuilder();
    }
    /**
     * 年月日レベルで比較を行う
     * @param date 比較対象のDate
     * @return 年月日比較で用いられる{@link DateMatcher}
     */
    public static DateMatcher isDate(Date date) {
        return new DateMatcher(date, DateField.ALL);
    }
    /**
     * 年比較を行う
     * @param year YYYY形式の日付
     * @return 年比較で用いられる{@link DateMatcher}
     */
    public static DateMatcher isYear(int year) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        return new DateMatcher(calendar.getTime(), DateField.YEAR);
    }
    /**
     * 月比較を行う
     * @param month 1~12までの月
     * @return 月比較を行う{@link DateMatcher}
     */
    public static DateMatcher isMonth(int month) {
        if (month > 12 || month < 1) {
            throw new IllegalArgumentException("対象外の月が設定されています");
        }
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.MONDAY, month -1);
        return new DateMatcher(calendar.getTime(), DateField.MONTH);
    }
    /**
     * 日比較を行う
     * @param day 1~31までの日
     * @return 日比較を行う{@link DateMatcher}
     */
    public static DateMatcher isDay(int day) {
        if (day > 31 || day < 1) {
            throw new IllegalArgumentException("対象外の日が設定されています");
        }
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, day);
        return new DateMatcher(calendar.getTime(), DateField.DAY);
    }

    /**
     * {@inheritDoc}
     * @see org.hamcrest.Matcher#matches(java.lang.Object)
     */
    public boolean matches(Object item) {
        Date date = (Date) item;
        if (this.entity == null ^ date == null) {
            if (this.entity == null) {
                this.message.append(" -> entity is null.");
            } else {
                this.message.append(" -> date is null.");
            }
            return false;
        }
        if (this.entity == null && date == null) {
            //TODO とりあえずどちらもnullの場合はfalseを返してメッセージを設定する
            this.message.append(" -> entity date both is null.");
            return false;
        }

        return this.field.is(this.entity, date);
    }

    /**
     * {@inheritDoc}
     * @see org.hamcrest.SelfDescribing#describeTo(org.hamcrest.Description)
     */
    public void describeTo(Description description) {
//        description.appendValue(this.entity);
        this.field.addDescription(description, this.entity);
        description.appendText(this.message.toString());
    }
}

ちょっと補足すると、DateMatcher#describeToでは、標準出力に出力する文字列を定義出来ます。
たとえばEclipseのJUnitでAssertionErrorが発生すると、以下のような出力が行われます。

java.lang.AssertionError: 
Expected: 
     got: 

description.appendValue(Object)でExpected:の値の部分に文字列が、
description.appendText(String)で上記に続く文字列が設定出来ます。

で、DateMatcherを作ったらテストクラスもあるわけで、以下の通りです。

import java.lang.reflect.Field;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

@RunWith(Parameterized.class)
public class DateMatcherTest {

    @Parameters
    public static Collection methods() {
        return Arrays.asList(new Object[][] {
                {null},
                {DateField.ALL},
                {DateField.YEAR},
                {DateField.MONTH},
                {DateField.DAY}
        });
    }
    private DateField field;
    
    public DateMatcherTest(DateField field) {
        this.field = field;
    }

    // 初期化の検証
    @Test
    public void コンストラクタの検証() throws Exception {
        DateMatcher matcher = new DateMatcher(new Date(), field);
        DateField  field = this.<DateField>getPrivateProperty(matcher, "field");
        assertThat(field, is(field));
    }
    @Test
    public void コンストラクタの検証_null() throws Exception {
        DateMatcher matcher = new DateMatcher(new Date(), null);
        DateField  field = this.<DateField>getPrivateProperty(matcher, "field");
        assertThat(field, is(DateField.ALL));
    }
    @Test
    public void isDateによるインスタンス生成() throws Exception {
        DateMatcher matcher = DateMatcher.isDate(new Date());
        DateField  field = this.<DateField>getPrivateProperty(matcher, "field");
        assertThat(field, is(DateField.ALL));
    }
    @Test
    public void isYearによるインスタンス生成() throws Exception {
        DateMatcher matcher = DateMatcher.isYear(2010);
        DateField  field = this.<DateField>getPrivateProperty(matcher, "field");
        assertThat(field, is(DateField.YEAR));
    }
    @Test
    public void isMonthによるインスタンス生成() throws Exception {
        DateMatcher matcher = DateMatcher.isMonth(11);
        DateField  field = this.<DateField>getPrivateProperty(matcher, "field");
        assertThat(field, is(DateField.MONTH));
    }
    @Test
    public void isDayによるインスタンス生成() throws Exception {
        DateMatcher matcher = DateMatcher.isDay(10);
        DateField  field = this.<DateField>getPrivateProperty(matcher, "field");
        assertThat(field, is(DateField.DAY));
    }
    @Test(expected=IllegalArgumentException.class)
    public void isMonth利用時の境界値の検証_MIN() {
        DateMatcher matcher = DateMatcher.isMonth(0);
    }
    @Test(expected=IllegalArgumentException.class)
    public void isMonth利用時の境界値の検証_OVER() {
        DateMatcher matcher = DateMatcher.isMonth(13);
    }
    @Test(expected=IllegalArgumentException.class)
    public void isDay利用時の境界値の検証_MIN() {
        DateMatcher matcher = DateMatcher.isMonth(0);
    }
    @Test(expected=IllegalArgumentException.class)
    public void isDay利用時の境界値の検証_OVER() {
        DateMatcher matcher = DateMatcher.isMonth(32);
    }
    
    
    // Matcherの検証
    @Test
    public void isDateの検証() {
        Date d = new Date();
        Date d1 = DateUtil.getDay(d);
        assertThat(d1, DateMatcher.isDate(d));
    }
    @Test
    public void isDateの検証_false() {
        Date d = new Date();
        Date d1 = DateUtil.getDay(DateUtil.moveDay(d, 1));
        assertThat(d1, not(DateMatcher.isDate(d)));
    }
    
    @Test
    public void isYearの検証_true() throws ParseException {
        Date d = DateFormater.toDate("2010/11/12");
        assertThat(d, DateMatcher.isYear(2010));
    }

    @Test
    public void isYearの検証_false() throws ParseException {
        Date d = DateFormater.toDate("2010/11/12");
        assertThat(d, not(DateMatcher.isYear(2011)));
    }

    @Test
    public void isMonthの検証_true() throws ParseException {
        Date d = DateFormater.toDate("2010/11/12");
        assertThat(d, DateMatcher.isMonth(11));
    }

    @Test
    public void isMonthの検証_false() throws ParseException {
        Date d = DateFormater.toDate("2010/11/12");
        assertThat(d, not(DateMatcher.isMonth(10)));
    }

    @Test
    public void isDayの検証_true() throws ParseException {
        Date d = DateFormater.toDate("2010/11/12");
        assertThat(d, DateMatcher.isDay(12));
    }

    @Test
    public void isDayの検証_false() throws ParseException {
        Date d = DateFormater.toDate("2010/11/12");
        assertThat(d, not(DateMatcher.isDay(11)));
    }
    @Test
    public void null値を含む検証0() {
        DateMatcher matcher = new DateMatcher(new Date(), null);
        assertThat(matcher.matches(null), is(false));
    }
    @Test
    public void null値を含む検証1() {
        DateMatcher matcher = new DateMatcher(null, null);
        assertThat(matcher.matches(new Date()), is(false));
    }
    @Test
    public void null値を含む検証2() {
        DateMatcher matcher = new DateMatcher(null, null);
        assertThat(matcher.matches(null), is(false));
    }
    
    
    // ユーティリティ
	// privateフィールドをリフレクションで取得する。
    public static <T> T getPrivateProperty(DateMatcher matcher, String propertyName) throws Exception {
        Field field = matcher.getClass().getDeclaredField(propertyName);
        field.setAccessible(true);
        return (T) field.get(matcher);
    }
}

良ければ使ってみてください。

Posted in program | タグ: , , , | Leave a Comment »

easybを学ぶ0

Posted by hikaruworld : 2010 3月 3

@t_wadaさんがRSpecの入門とその1歩先へ という日記を書いていたので、
Rubyが使えない自分はJavaでeasybを使ったBDD導入の話を備忘録的に書いていこうと思います。

ちなみにマイコミの記事が大変参考になります。多謝。

個人的にはTDDだけじゃなくてBDD+TDDとかSDD+BDD+TDD的な流れになっていけば、
みんなで情報交換できて素敵なのにと思ってたりします。

eclipse上に環境構築(推奨)

easybってなんぞ?というお話はマイコミの記事なりWikipediaを読んでもらうとして、
とりあえずeclipse上に環境構築です。

easybはGroovyという、Java言語を関数的に使えるように拡張された言語で書かれているので、
eclipseさえインストールしておけば基本的にはGroovyとeasybのライブラリにパスを通すだけで、
簡単に動かすことが可能です(コマンドライン,Ant,etc…)。

…可能です、groovyのプラグインをインストールしておかないと、コード補完のないJava的なコードを
書く必要になるので、プラグインをインストールしておきましょう。
(いくら関数型的な記述が可能だからといって、import文を手動でとかはカンベンです)

Groovy-Eclipseのインストール

EclipseのGroovyプラグインGroovy-Eclipse をインストールします。
英語ですが、わかりやすい手順がこのURLにあります。
http://docs.codehaus.org/display/GROOVY/Install+Groovy-Eclipse+Plugin
なお、eclipse3.5か3.4.2以上にする必要があります。

3.4.1だとGroovyプラグインが動かないので注意が必要です。
(私はこれでハマりました)
eclipseのupdatesiteがあるので楽ちんです。
http://dist.springsource.org/release/GRECLIPSE/e3.5/

easybのインストール

ここからダウンロードしたeasyb-0.9.6.tar.gz を解凍してeclipseからパスを通すだけです。
楽チン。
以下の3つがありますが、Groovyプラグインをインストールしている場合は、groovy-1.6.4.jarは不要です。

  • commons-cli-1.2.jar
  • easyb-0.9.6.jar
  • groovy-1.6.4.jar

実行の設定

xDDなので、サクサク実行出来ることが大切です。
簡単に実行出来るように、以下のいずれかの設定を行っておきます。

  1. Runを使ってmainクラスの実行
  2. Ant経由で実行
  3. easybのeclipseプラグインを導入して実行

http://www.easyb.org/running.html

Runを使ってmainクラスの実行

eclipesのRunとして実行できると細かい単位で実行できて便利ですので設定しておきましょう。
0.サンプルプロジェクトとしてこんな感じに作りました。

1.「実行の構成」で設定を行います。

2.Groovyスクリプトを設定します。
「メイン・クラスの検索時にシステム・ライブラリーを組み込む」にチェックを入れて、
「org.easyb.BehaviorRunner」を設定します。

3.引数に実行対象のgroovyファイルを指定します。

以上で設定はOKです。
あとは必要に応じて実行しましょう。
ちなみに実行するとこんな感じでコンソールに出力されます。

Running hello story (HelloStory.groovy)

Scenarios run: 1, Failures: 0, Pending: 1, Time elapsed: 1.489 sec

1 behavior ran (including 1 pending behavior) with no failures

Ant経由で実行

一括実行はやはりAntで。後でHudsonでも設定したりするので、先に確認しておきましょう。
プロジェクトルートにこのような感じのbuild.xmlを作成します。

<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="init" basedir=".">
	<target name="init">
		<!-- easybというタスクを登録 -->
		<taskdef name="easyb" classname="org.easyb.ant.BehaviorRunnerTask">
			<classpath id="build.classpath">
				<pathelement location="easyb-0.9.6/commons-cli-1.2.jar"/>
				<pathelement location="easyb-0.9.6/easyb-0.9.6.jar"/>
				<pathelement location="easyb-0.9.6/groovy-1.6.4.jar"/>
			</classpath>
		</taskdef>

		<!-- easybの実行 -->
		<easyb>
			<!-- easybで利用するクラスファイルへパスを通す -->
			<classpath>
				<path refid="build.classpath" />
			</classpath>
			<!-- レポートの出力形式を指定 -->
			<report location="target/story.txt" format="txtstory" />
			<report location="target/story.txt" format="html" />
			<!-- 振る舞いクラスを指定 -->
			<behaviors dir="src">
				<include name="**/*Story.groovy"/>
			</behaviors>
		</easyb>
	</target>
</project>
実行すると以下のようなコンソールログが出力されます。

Buildfile: /Users/***/***/workspace/Sample/build.xml
init:
[easyb] easyb is preparing to process 1 file(s)
[easyb] Running hello story (HelloStory.groovy)
[easyb] Scenarios run: 1, Failures: 0, Pending: 1, Time elapsed: 0.74 sec
[easyb] 1 behavior ran (including 1 pending behavior) with no failures
[easyb] easyb execution passed
BUILD SUCCESSFUL
Total time: 5 seconds

指定したレポート形式で実行結果が出力されます。
今回は割愛しますが、xml形式やprettyprintといった形式で出力することも可能です。

easybのeclipseプラグインを導入して実行

これが一番簡単です。

easyb のサイトにeclipseプラグインとして実行する方法がのっています。
この方法はupdatesiteでインストールできるのでお手軽です。
http://easyb.googlecode.com/svn/trunk/eclipse-plugins/org.easyb.eclipse.updatesite/

easybのeclipseプラグインをインストールすると、「新規」からeasybのファイルを生成出来るようになります。

「story」はStoryDSL、「指定」はBehavior DSLのテンプレートを生成してくれます。
実行に関しては作成したeasybファイルを右クリックして実行することで実行可能です。

実行結果はこんな感じになります。

Running A story (A.story)
Scenarios run: 1, Failures: 0, Pending: 0, Time elapsed: 1.0 sec

1 behavior ran with no failures

今日は環境構築まで。
次は、実際の実施手順に進みます。
以上です。

Posted in program | タグ: , , | Leave a Comment »

JavaWebStartの仕様?とバグでどつぼった

Posted by hikaruworld : 2010 3月 2

JavaWebStartの件で質問を受けていて調査していたんだけど、
Javaのコンボ攻撃を食らって、ピヨピヨしていたので書いておきます。ピヨピヨ…

JavaWebStartのショートカット作成の挙動

JavaWebStartをWeb上のリンクから初めて起動した場合に、インストールプロセスが走ります。
同時に、設定によってショートカットが作成(あるいは作成するか確認)されます。

その後、ショートカットのみを削除します。
そうしてもう一度、Web上のリンクから起動を行います。
すると(おそらく)インストールプロセスが走らずにローカルアプリの起動処理が走るみたいです。

# これって、JavaのコントロールパネルからJavaWebStartアプリをインストールしても、
# 削除済みリソースに残りますよね。こっちの場合はどうなるんだろ(未確認)

JavaのUpdateの挙動

Javaのruntimeをupdateする場合、update時にショートカットが
削除されてしまうバグ
がデグレードしているようです。

この現象は,jre1.6.18,jre1.6.17,jre1.6.16辺りまで1つ古いバージョンから
次のバージョンへのUpdate時に発生する事を確認しました。
※jre1.6から1つずつバージョンUPしながら確認してみたんですが、実際にはjava1.6.11で再発しています。
なお、具体的な確認手順は以下の通りです。

  1. 任意のjreをインストール
  2. JavaWebStartのアプリをインストール
  3. PCを再起動
  4. jreをUpdate

環境は、WindowxXP SP3 適用済みのものをVirtualPC上で実行して確認しています。

このバグjre1.6.02でfixされているようですが、
上記のバージョンのいずれでも発生しているためデグレードされているようです。
現象としては、JavaWebStartのショートカットが削除されるだけなので影響範囲は小さいと言えるかもしれません。

バグと仕様であわせて1本orz…

ところがどっこい、これらの現象が重複して発生すると、やーなことが発生します。
つまり…

  1. JavaWebStartをインストール
  2. jreをUpdate
  3. ショートカットが削除される
  4. ショートカットが削除されたので、再度JavaWebStartをインストール
  5. ショートカットが削除されただけなので、インストールプロセスが走らずにローカルへインストール済みのアプリが起動
  6. ショートカットが作成されない

という状態に陥ります。

以上ですorz….ピヨピヨ…

Posted in program | タグ: , | Leave a Comment »

さようなら、SunMicrosystems.

Posted by hikaruworld : 2010 1月 29

今日の昼ごろ、Javaをアーカイブからダウンロードしていたら、
左下に【Oracle】のロゴマークが出ていることに気が付きました。

いつの間にか…と思ってダウンロードリンク画面があるところまで行くと、
Oracleのサイトに(URLはjava.com)。
???とりあえずダウンロードして、もう一度アーカイブに行ってみると、
今度はヘッダーから何からoracleにサイトになっていました。

…何というか、非常に感慨深いものがあります。

CEOの話で
「Sunという会社はなくなるけど、Sunというブランドは続いていく」
といった話を最後にされていたようですが、まさかある意味その瞬間を見ることになるとは思わなかったので、
なんともぐっと来るものがあります。

プログラミング言語で初めてJavaをはじめてたかだか5、6年の私ですが、
今後ともJavaにはお世話になっていくと思いますので、
健全にJavaというプログラム言語が進化していくことをのぞみます。

SunRIP
from http://blogs.sun.com/jag/

# java.netや、topページはまだ変わっていないようですが、そのうち置き換えられていくのですかね。
# TOPページ自体は昨日変わったらしいですね。まだまだ変わっていない部分もあるようですが…

Posted in program | タグ: | Leave a Comment »

私がJavaのバイトコードをJavassistで操作する時

Posted by hikaruworld : 2010 1月 28

最近、既存コードのテストコードを書きまくっています。
その中で、Easymock(classExtension)などを用いてMock化している場合に、
finalクラスをMock化できず困っていました。

何かいい方法がないかなと思っていたらhudsonの開発者である川口さんが
jaavassistを利用してfinalを除去しているという記事があり、
なるほどと思って自分もその方向で対応を行うことにしました。

その際に「なぜテストを書くのか」「どうしてそんな書き方をするのか」
ということを色々考えていたので、簡単に脳みそを整理してみます。

バイトコードが使われるとき

主にフレームワーク内部やIDEで利用されている技術と聞いています。
つまり大多数の一般的な開発者には使われないという認識です。

なぜテストにおいてバイトコード操作やリフレクションを使うのか

私の主観としては、リフレクションやバイトコード操作は通常の業務アプリケーションの実装で
頻繁に使うべきでないと考えています(少なくとも自分の周囲の現場では)。
理由としては以下のようなものが挙げられます。

  • パフォーマンスの問題
  • ソースコード可読性の低下
  • IDE(eclipseなど)によるサポートが追いついていない
  • 技術レベルの要求
  • リフレクションやバイトコードを操作するくらいなら設計を見直すべき

その一方で、テストコードにおいては積極的に活用すべき技術だと思います。
レガシーコード(ここでは古いコードの意味でなく「テストコードがないプログラム」という意)は、
テスタビリティが低く実装されていたため、
既存のプログラムが正常に動くことを保障した上でリファクタリングを行う必要がありました。
※テストによってセーフティネットを提供する必要が大前提としてあるからと考えています。

byteコード操作

javaのバイトコードを理解せずにバイトコード操作を行うことは自分には敷居が高いため、
javassistというライブラリを利用させていただきました。以下のリンクに詳しく説明が書かれています。

自分が必要になった状況

テスト時にバイトコード操作が必要になった状況は以下のような状況でした。

  • finalクラスをmock化したい(Easymockではfinalクラスをmock化できない)
  • privateメソッドをmock化したい

javassistのライセンス

MPL (Mozilla Public License) と LGPL (GNU Lesser General Public License) のデュアルライセンスだそうです。
テストコードですのでリリース時はアプリケーションに同梱しなければ問題ないと思いますが、
注意しておきたいところです。

Posted in program | タグ: , , , | Leave a Comment »

HudsonをバージョンUPしたらNoClassDefFoundError

Posted by hikaruworld : 2010 1月 19

Hudsonさんには毎日お世話になっています。
そんなHudsonさんを最近VersionUpしていなかったので、
今回ver1.323からver1.341にバージョンUPしました。

私の家では彼(プロジェクトによっては彼女ですが)はTomcat上で動いていて、バージョンは6.0.18になります。
再起動後、以下のようなエラーになりました。

SEVERE: Failed to initialize Hudson
java.lang.NoClassDefFoundError: hudson/Main (wrong name: Main)
at java.lang.ClassLoader.defineClass1(Native Method)
…以下略

とりあえずググって見ると以下のURLを発見。
http://n4.nabble.com/Error-deploying-1-324-to-tomcat-td361411.html
同様の件のようなのでスレッドを追っていると、川口氏のこんなコメントが。
http://n4.nabble.com/Error-deploying-1-324-to-tomcat-tp361411p361415.html

Can you go to $TOMCAT_HOME/webapps/hudson/WEB-INF/lib and run

javap -classpath hudson-core-*.jar hudson.Main

… to make sure you see “public class hudson.Main extends
java.lang.Object” ?

ふむふむ、実行クラスを逆アセンブラしてMainメソッドがあるか確認してみろと。
これに従って実行してみる。
以下ログ。

Compiled from "Main.java"
public class hudson.Main extends java.lang.Object{
    public static boolean isUnitTest;
    public hudson.Main();
    public static void main(java.lang.String[]);
    public static int run(java.lang.String[])       throws java.lang.Exception;
    public static int remotePost(java.lang.String[])       throws java.lang.Exception;
    static {};
}

ちゃんとMainが存在しているようです。
改めて、Tomcatを再起動。

問題なくアクセスできてしまいました。
うーん、原因が謎。。。

ところで、Hudsonのバージョンをver1.341にバージョンアップすると

Get Support Subscription
Commercial support subscription available from Sun Microsystems.

というものが表示されていました。

いつの間にさサポート契約まで出来ていたとは。。。
結構なお値段ですが、企業とかであるとその価値は十分ありそうな気がします。

Subscription Level Master # of additional slaves Price
Basic 1 1 $999/yr
Silver 1 10 $2999/yr
Gold 1 25 $5999/yr

Posted in program | タグ: , , | Leave a Comment »