2009年12月9日水曜日

JPA/Toplink/Eclipse/Maven

Toplink 上で JPA を動かす Getting Started。

以下の環境で、"Hello, World!"を出力するものを作ってみる。
  • Eclipse 3.5 + m2Eclipse
  • Maven 2.2.1
  • MySQL 5.0.45
  • Toplink-essentials 2.0-35
データベースのセットアップは Hibernate のときと同じ。

■ プロジェクト生成
  • [New]->[Other...]->[Maven Project]で新規プロジェクト作成。以下のような感じで、適当にArtifact Id 等を入力。
    group idnet.yasuabe.studies
    artifact idjpa-tl-test1
    version0.0.1-SNAPSHOT
    packagenet.yasuabe.studies.jpa_tl_test1
  • POM のdependencies を以下のようにする。
      <dependencies>
    <dependency>
    <groupId>toplink.essentials</groupId>
    <artifactId>toplink-essentials</artifactId>
    <version>2.0-35</version>
    </dependency>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.10</version>
    </dependency>
    </dependencies>

    ※もしかしたら、空の toplink-essentials が落ちてくるかもしれない。そうなったら、以下のようにローカルのリポジトリに手作業でインストール。
    • ここから toplink-essentials-2.0-35.jar、toplink-essentials-2.0-35.pom、toplink-essentials-2.0-35-sources.jar を落とす
    • 以下のコマンドでインストール(一行で)。
      C:\download\tmp>mvn install:install-file \
      -DgroupId=toplink.essentials \
      -DartifactId=toplink-essentials \
      -Dversion=2.0-35 \
      -Dpackaging=jar \
      -Dfile=toplink-essentials-2.0-35.jar \
      -Dsources=toplink-essentials-2.0-35-sources.jar \
      -DpomFile=toplink-essentials-2.0-35.pom
  • src/test/java 下のAppTest.java を捨てる

■ Hello World 作成と確認
Hibernate のときと同じ Appクラス と T1クラスを使う。実行すると、Hello, World! が出力されているのがわかる。

2009年12月8日火曜日

JPA/Hibernate/Eclipse/Maven

Hibernate 上で JPA を動かす Getting Started。 (後で Toplink もやってみる。) 以下の環境で、"Hello, World!"を出力するものを作ってみる。
  • Eclipse 3.5 + m2Eclipse
  • Maven 2.2.1
  • MySQL 5.0.45
  • Hibernate 3.3.2
■ データベースの用意
mysql> create database db1;
mysql> connect db1;
mysql> create table t1 (c1 int primary key, c2 varchar(20));
mysql> insert into t1 (c1, c2) values(100, 'World');
mysql> select * from t1;
+-----+-------+
| c1  | c2    |
+-----+-------+
| 100 | World |
+-----+-------+
■ プロジェクト生成
  • [New]->[Other...]->[Maven Project]で新規プロジェクト作成。アーキタイプに maven-archetype-quickstart 1.0 を選択。Artifact Id 等は適当に入力。ここでは以下のようにした。
    group idnet.yasuabe.studies
    artifact idjpa-hib-test1
    version0.0.1-SNAPSHOT
    packagenet.yasuabe.studies.jpa_hib_test1
  • POM のdependencies を以下のようにする。
    <dependencies>
       <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-entitymanager</artifactId>
          <version>3.4.0.GA</version>
          <exclusions>
             <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
             </exclusion>
          </exclusions>
       </dependency>
       <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-simple</artifactId>
          <version>1.5.10</version>
       </dependency>
       <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>5.1.10</version>
       </dependency>
       <dependency>
          <groupId>org.hibernate</groupId>
          <artifactId>hibernate-core</artifactId>
          <version>3.3.2.GA</version>
       </dependency>
    </dependencies>
  • src/test/java 下のAppTest.java を捨てる
■ Hello World 作成
  • [Build Path]->[New Source Folder...]で、src/main/resource を追加。
  • 上記ソースフォルダに、META-INF/persistence.xmlを作成。
    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="1.0" 
        xmlns="http://java.sun.com/xml/ns/persistence" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
      <persistence-unit name="em1" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
          <properties>
             <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
             <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
             <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/db1"/>
          </properties>
      </persistence-unit>
    </persistence>
    ユーザ名/パスワード、その他のプロパティを指定する場合は[ここ参照]
  • こんなエンティティクラスを書く
    package net.yasuabe.studies.jpa_hib_test1;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    
    @Entity
    public class T1 {
      @Id
      private int c1;
      private String c2;
      
      public int getC1() { return c1;  }
      public void setC1(int c1) { this.c1 = c1; }
    
      public String getC2() { return c2;  }
      public void setC2(String c2) { this.c2 = c2; }
    }
  • App のmain() を書き換える
    public static void main(String[] args) {
      EntityManagerFactory emf = Persistence.createEntityManagerFactory("em1");
      EntityManager em = emf.createEntityManager();
    
      T1 t1 = em.find(T1.class, 100);
      System.out.printf("Hello, %s!%n", t1.getC2());
    
      em.close();
      emf.close();
    }
■ 確認 実行すると、Hibernate のログ出力に混じって、コンソールビューに Hello, World! が出力されているのがわかる。いったん動いたら、後はまあ楽。 ちなみに 新規 Maven Project で jpa-maven-archetype というアーキタイプも使える(http://maven.rodcoffin.com/repo)。上のやり方でやった場合とちょっと違う構成になる。

2009年12月7日月曜日

Scala / multiple classification

multiple classification (多重分類/多重型付け)について、Scala ベースで考えてみた。 「Analysis Patterns」 から抜粋した上のクラス図(Odell表記)の意味は、
  • 顧客のインスタンスは、サブクラスである個人顧客または法人顧客のいずれかのインスタンスである。
  • 顧客のインスタンスは、サブクラスである重要顧客インスタンスである場合もあり、そうでない場合もある。
上図の分析モデルでは2つの系統の重なりを禁じていないので、ベン図で書くとこんな風に重なりが生じる。 こういうのを、オブジェクト指向分析の用語で multiple classification と言う。生活世界の感覚では、別段、不自然な事ではないけど、従来の single-classification ベースの言語で考えると、ちょっと面倒くさくなる。 つまり重なった部分についても、重要個人顧客、重要法人顧客といったように、組み合わせの分だけクラスを定義する事になる。これを避けて、種別を示す列挙定数を持たせるなどするけど、結局、分析と実装のギャップが広がってしまう。 で、前に Scala を少し調べたとき、分析と実装を乖離させる事なく、上述のような複数系統の型付けを自然に実装できそうな感じがしたので、ちょっと簡単なコードで確かめてみたい。 以下のようななんちゃって仕様を仮定する。
  • 顧客(Customer)は値引き率をあらわすメソッド discount() を持つ。
  • サブクラス 個人顧客(PersonalCustomer)の discount()は、2月生まれの人を2割引する
  • サブクラス 法人顧客(Corporation)の discount()は、従業員10人未満の法人を1割引する。
  • サブクラス 重要顧客の discount()は、個人/法人の discount() で値引きされた率に加えて、さらに3割引する。例えば 2月生まれの重要個人顧客は 2割引の 3割引で 4.4割引になる(1-(1-0.2)*(1-0.3)=0.44)。
こんなコードを書いてみた。
class Customer {
 def discount: Double = 0
}
class PersonalCustomer(val birthMonth: Int) extends Customer {
 override def discount: Double = if (2 == birthMonth) 2 else 0
}
class Corporation(val employeeNum: Int) extends Customer {
 override def discount: Double = if (10 > employeeNum) 1 else 0
}
trait PriorityCustomer extends Customer {
 override def discount: Double = (100 - (10 - super.discount) * 7) / 10
}
以下、結果。
scala> new PersonalCustomer(2).discount
res62: Double = 2.0

scala> (new PersonalCustomer(2) with PriorityCustomer).discount
res63: Double = 4.4

scala> new Corporation(5).discount
res64: Double = 1.0

scala> (new Corporation(5) with PriorityCustomer).discount
res65: Double = 3.7
とりあえず計算はできたらしい。重なり部分のクラスを個別に定義しなくても、型付けを多重化して仕様を合成できた。ただし、インタプリタでなくソースコードでの生成を考えた場合、組み合わせる型を with に続けてハードコードするのではなく、実行時に与えられるようにしたい。そうでないと、実質、個人重要顧客/法人重要顧客に相当する無名クラスを定義しただけにすぎない感じがする。これじゃ結局、single-classification だ。うーん、もうちょい調べないと・・・

2009年12月6日日曜日

SOA-RAF その他

InfoQ の この記事を読んで、いくつか知らない用語が出てきたので、メモっておく。

SOA-RAF
  • Reference Architecture Foundation for SOA
  • OASIS 仕様だけど、OMG の仕様によくあるような、かなり抽象度の高いメタモデル表現を SOA に適用した感じ(MOF じゃなくて UML 2.0)。
  • エコシステム(3章)・実現(4章)・所有(5章)の3つの切り口による記述。全119ページだけど、明解な構成で読みやすそうな印象。
SOA-RM
  • Reference Model for SOA
  • 31ページの短い仕様書。Figure 1に、他のSOA 関連仕様/作業との 関係性が図示されていて、上記 SOA-RAF などより、更にひときわ抽象度の一番高いところから、一群の SOA 関連仕様/作業に指針を与えるという位置づけ。
  • チラッと眺めただけだけど、ネットワークの OSI だとか他ジャンルの「参照モデル」も参考にされていると予想。役割もおそらくそんな感じなんだと思う。
SOAML
  • SOA Modeling Language
  • UML の SOA拡張。標準プロファイルの仕組みを準用。
  • 以下の各種メタモデル仕様との統合も、ゴールの一つ。

    • SBVR:semantics of business vocabulary and business rules specification
    • OSM:Organization Structure Metamodel
    • ODM:Ontology Definition Metamodel
    • BPDM:Business Process Definition Metamodel
    • BPMN 2.0:Business Process Modeling Notation

MIDI/Applet/Javascript/canvas

以前のポストで、MIDI ファイルじゃなくて MIDI 信号を、MIDIデバイスに直接送る Java Applet を作って、これを JavaScript から操作する実験をした。また、Blogger で HTML5 canvas を使うやり方について、このポストで目処をつけた。

今日はこの二つの連携させて、簡単な音感訓練ツールを作ってみた。

ただし、今回は FireFox メインで、他ブラウザは度外視する事にした。というのも、IE はcanvas 未対応だから、そのかわりに excanvas.js を使うと、マウスクリックイベントの座標が、canvas の座標じゃなくて個々の図形要素内の座標で返されるらしく、この違いを吸収するためのスッキリした解決策がどうにも見当たらなかったから。

Opera も けっこう挙動が違っていて、見捨てることにした。前回は できるだけクロスブラウザ になるよう頑張ったが、今やりたいことと関係無いので、さしあたり保留。というわけで、Firefox 限定って事で割り切った。

動作は、以下のような感じにした。
  • マシンが4オクターブの範囲(大譜表の真ん中のドから上下2オクターブ)からランダムに選んだ音を聞いて、それを当てる。
  • 緑色の部分をクリックすると問題の音が出力される。
  • 音名に対応する白黒の丸をクリックすると、その音高の音が鳴り、正解・不正解が表示される。
  • 正解なら、正解数がインクリメントされ、次の問題となる音が改めて選択される。
  • 不正解なら、問題となる音はそのまま留保され、誤答数がインクリメントされる
  • 正答でも誤答でも、正答率が更新される。
IE お断り
var canvas=document.getElementById("canvas091206");var ctx = canvas.getContext("2d");cx = 120;cy = 120;r1 = 80;r2 = 16;noteChar = ['C','','D','','E','F','','G','','A','','B'];ctx.fillStyle = "rgb(0,0,0)";ctx.textBaseline = 'middle';for (i=0; i<12; i++) {ctx.beginPath();a1 = (i * 30) * Math.PI / 180;cx + r1 * Math.sin(a1);ctx.arc(cx + r1 * Math.sin(a1), cy - r1 * Math.cos(a1), r2, 0, Math.PI * 2, false);if (1 ==i || 3 ==i|| 6==i||8==i||10==i) {ctx.fillStyle = "rgb(0,0,0)";ctx.fill();} else {ctx.fillStyle = "rgb(255,255,255)";ctx.fill();ctx.strokeStyle = "rgb(0,0,0)";ctx.strokeText(noteChar[i], cx + r1 * Math.sin(a1) - 4 , cy - r1 * Math.cos(a1));}}


本当、IE、なんとかならんかの・・・

2009年12月5日土曜日

JNDI / LDAP

RMI とJNDI との連携を試してみたいが、その前に 普通の Serializeable なオブジェクトを JNDI に登録する方法を確認したい。以下、JNDI を使って、HashMap オブジェクトを LDAP コンテキストに登録した記録。 (Cygwin の OpenLDAP を動かすやり方と、ここで使っている LDAP設定は前ポスト参照) 以下の試行では、Eclipse 3.5 Galileo から JUnit 4を使って、@Test メソッドを個別に実行する方法でやってみた。(@Test メソッドごとに実行するやり方は、Eclipse の Outline ビューでメソッドを選択して、コンテキストメニューから [Run As]/ [JUnit Test]。) コードは以下のようなものを用いた。
import ・・・略

public class App {
  private static Context ctx = null;
  
  @BeforeClass
  public static void connect() throws Exception {
    Hashtable<String, String> env = new Hashtable<String, String>(11);
    env.put(Context.INITIAL_CONTEXT_FACTORY,
        "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITest,dc=example,dc=com");
    env.put(Context.SECURITY_AUTHENTICATION, "simple");
    env.put(Context.SECURITY_PRINCIPAL, "cn=Manager,dc=example,dc=com");
    env.put(Context.SECURITY_CREDENTIALS, "secret");

    ctx = new InitialContext(env);
  }
  @AfterClass
  public static void closeContext() throws Exception {
    if (null != ctx) ctx.close();
  }
  @Test
  public void bind() throws Exception {
    Map<String, String> map = new HashMap<String, String>();
    map.put("keyA", "valueA");
    ctx.bind("cn=map", map);
  }
  @SuppressWarnings("unchecked")
  @Test
  public void lookup() throws Exception {
    Map<String, String> map = (Map<String, String>) ctx.lookup("cn=map");
    assertEquals("valueA", map.get("keyA"));
  }
  @Test
  public void unbind() throws Exception {
    ctx.unbind("cn=map");
  }
}
これを以下の手順で動かして、どうなるか観察する。
  • まず lookup() を実行する。未登録なので NamingException が発生する事を確認
  • 次に bind() を実行し、例外が出ない事を確認。また実際に o=JNDITest の下に cn=map エントリが作られている事を、LDAP Admin で確認
  • 再び lookup() を実行し、今度は例外も無く assertEquals が成功する事を確認
  • 最後に unbind() を実行。例外無く終了し、LDAP Admin でみると cn=map エントリが消えている事を確認
これで RMI とJNDI の組み合わせを試す準備ができた。

xslt, 平均と分散

XSLT (v1.0)で 平均と分散を計算してみる。
<?xml version="1.0" encoding="UTF-8"?>
<list>
<item>30</item>
<item>10</item>
<item>40</item>
<item>20</item>
</list>

上の XML を読み込んで、下の XML を出力したい。
<?xml version="1.0" encoding="UTF-8"?>
<Average>25</Average>
<Varianece>125</Varianece>

やり方はいろいろあるが、再帰呼び出し一往復で計算が終わるようにしてみた。つまり値を足しこみながら端まで計算して、突き当たりで平均を計算し、その平均値を使って二乗和を合算しながら戻り、帰り着いたところで分散を出す。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/list">
<xsl:variable name="result">
<xsl:call-template name="stat"> <xsl:with-param name="n" select="item" /> <xsl:with-param name="s" select="0" /> <xsl:with-param name="c" select="0" /> </xsl:call-template>
</xsl:variable>
<xsl:variable name="sv" select="substring-before($result, ':')" />
<xsl:variable name="rest" select="substring-after($result, ':')" />
<xsl:variable name="count" select="substring-before($rest, ':')" />
<Average>
<xsl:value-of select="substring-after($rest, ':')" />
</Average>
<Varianece>
<xsl:value-of select="$sv div $count" />
</Varianece>
</xsl:template>
<xsl:template name="stat">
<xsl:param name="n" />
<xsl:param name="s" />
<xsl:param name="c" />
<xsl:choose>
<xsl:when test="$n">
<xsl:variable name="t">
<xsl:call-template name="stat">
<xsl:with-param name="n" select="$n[position() > 1]" />
<xsl:with-param name="s" select="$s + $n[1]" />
<xsl:with-param name="c" select="$c + 1" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="sv" select="substring-before($t, ':')" />
<xsl:variable name="rest" select="substring-after($t, ':')" />
<xsl:variable name="a" select="substring-after($rest, ':')" />

<xsl:value-of select="concat(($n[1]-$a)*($n[1]-$a)+$sv, ':', $rest)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('0:', $c, ':', ($s div $c))" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
(Eclipse で確認した)

各段階で生じる複数の戻り値を、いったん連結して一つながりの文字列にして返して、呼び出し側で文字列を分割して数値を復元したりしているのが、ちょっとコード的に汚い。

XSLT 2.0 では、結果ツリーフラグメントを普通のノードセットとして扱えるらしいけど、今回はパスした。(Eclispe のプラグインで、なんだか上手く動かなかった。)