2009年10月31日土曜日

m2eclipse/WTP/Struts2

前のポストで、先に Dynamic Web Project を作って、それをベースに Maven の依存性管理を与えるやり方で、WTP と m2eclipse を連携させてみた。今度はその逆をやってみる。 ネタとしては、以前 JSF2.0 でやったような、サーバ時間を表示する Getting Started 的な Web アプリを、Struts2 で作ってみる。 各プロダクトのバージョンは、WTP 3.1.1、m2eclipse 0.9.8、Maven 2.2.1、Tomcat 6.0.20、Eclipse 3.5。 ■ プロジェクトの準備
  • maven-archetype-webapp アーキタイプで Maven Project を作成する。(struts2-archetype-starterは、今回はやめておく。)
  • Navigator ビューから".project"ファイルを開いて<natures/>に以下の行を追加する。(Project Facetを設定できるようにするため。)
    <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
プロジェクトのプロパティを開いて以下の作業
  • Project Facet で Java にチェックを入れる。
  • そのまま Dynamic Web Module にもチェックを入れ、バージョンを 2.5 にし、Runtime タブで Apache Tomcat v6.0を指定(Server Runtime 構成済みの前提)。
  • ソースとアウトプットが以下のようになるよう設定する。(test関連フォルダ は省略。)
    sourceoutput
    src/main/javatarget/classes
    src/main/resourcestarget/classes
  • プロパティを一回閉じて開きなおす。
  • さっきまで無かった Java EE Module Dependencies があるので、これを選択して Maven Dependency にチェック
コンテントフォルダ関連の修正
  • Navigator ビューから".settings/org.eclipse.wst.common.component"ファイルを開いて "WebContent" を"src/main/webapp"に変更する
  • WebContent の中身(META-INF, WEB-INF)をsrc/main/webapp にコピーしてから、WebContent を削除する
Maven から WTP への作業はここまで。 ■ アプリを書く 以下で、確認用の小さな Struts2 アプリを作る。
  • pom.xml に以下の dependency を追加(2.1.8 は 2009/10/30 現在の最新)
    <dependency>
      <groupId>org.apache.struts</groupId>
      <artifactId>struts2-core</artifactId>
      <version>2.1.8</version>
    </dependency>
    
  • 適当にパッケージを作って以下の内容の ServerTimeAction クラスを作成
  • package net.xad.web.struts2.trial2;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import com.opensymphony.xwork2.ActionSupport;
    
    public class ServerTimeAction extends ActionSupport {
       private static final long serialVersionUID = 1L;
       private static final String FORMAT = "yyyy/MM/dd HH:mm:ss";
       private String serverTime;
       @Override
       public String execute() {
          serverTime = new SimpleDateFormat(FORMAT).format(new Date());
          return SUCCESS;
       }
       public String getServerTime() {
          return serverTime;
       }
    }
  • src/main/resources 下に以下の struts.xml を作成する。
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
           "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
           "http://struts.apache.org/dtds/struts-2.0.dtd">
    <struts>
        <package name="net.xad.web.struts2.trial2" namespace="/" extends="struts-default">
            <action name="serverTime" class="net.xad.web.struts2.trial2.ServerTimeAction">
                <result>/index.jsp</result>
            </action>
        </package>
    </struts>
    
  • web.xml を、以下のように編集する
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     id="WebApp_ID" version="2.5">
      <display-name>struts-trial1</display-name>
      <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
      </filter>
      <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
      <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
      </welcome-file-list>
    </web-app>
  • index.jsp を書き替える
    <%@ taglib prefix="s" uri="/struts-tags" %>
    <html>
      <body>
        <h2><s:property value="serverTime"/></h2>
      </body>
    </html>
    
できた。 ■ 確認 http://localhost:8080/struts-trial2/serverTime.action で時間が表示されるはず。F5 押下で更新。

2009年10月30日金曜日

galileo/m2eclipse/WTP 連携

以下、m2eclipse と WTP を連携させるやり方。ちょっと手間がかかるのと、それほど自明な手順でもないので、以下メモしておく。

■ 環境と前提
  • Eclipse 3.5
  • Maven 2.2.1
  • m2eclipse が入っている事
  • WTP が入っている事
  • Server Runtime Environment でApache Tomcat v6.0が構成されている事

■ 作り方
◆ Dynamic Web Project の作成
  • 1ページ目
    • プロジェクト名を指定。
    • Target Runtime は 構成済みの Apache Tomcat v6.0。その他はデフォルトのまま。
  • 2ページ目
    • src を削除して以下を追加
      • src/main/java
      • src/main/resources
      • src/test/java
      • src/test/resources
    • Default output folder→target/classes
  • 3ページ目
    • Content directory を WebContent から /src/main/webapp に追加

◆ プロジェクト上のコンテキストメニューから[Maven]→[Enable Dependency Management]を実行。ダイアログが開くのでgroup id, artifact id, version 等を適当に入力。packaging はwar。

◆ プロジェクトのプロパティを設定
  • Java Build Path
    Source タブ で、ソースとアウトプットの関係が以下のようになっている事を確認。違っていたら調整する
    src/main/javatarget/classes
    src/main/resourcestarget/classes
    src/test/javatarget/test-classes
    src/test/resourcestarget/test-classes
  • Java EE Module Dependencies
      → Maven Dependencies にチェックを入れる

※Compiler level が低い的なエラーが表示されていたら、プロジェクトのプロパティで Java compiler level を上げる

■ 確認
  • webapp 直下に index.jsp を作成。<body/>内に以下の一行を追加。
  •   <%=com.google.inject.Guice.class.toString() %>
  • プロジェクトのコンテキストメニューから、[Maven]->[Add Dependency]で guice-2.0.jar を追加
  • プロジェクトのコンテキストメニューから[Debug As]->[Run on Server]で起動
  • Eclispe 上のブラウザに、"class com.google.inject.Guice"と表示されるのを確認。
  • [Run As]->[Maven package]で war を作成し、コマンドラインから、"jar tvf ファイル名"で中身を確認(特に WEB-INF/lib の下)

2009年10月29日木曜日

JSF2.0/Ajax/Tomcat6.0/Eclipse3.5

JSF2.0 ajax の、簡単な getting started 的なものを作ったので記録。 ■ 環境
  • Eclipse 3.5
  • Tomcat v6.0:(Eclipse の Servers ビューでこの定義を追加しておく)
  • JSF 2.0:mojarra-2.0.0-RC-binaryに入ってるやつ。https://javaserverfaces.dev.java.net/ から持ってこられる。
■ 作る
  • Dynamic Web Project を作成。プロジェクト名はここでは trial1 とした。Tomcat の場合、デフォルトでこの名前がコンテキストパスに使用される。
  • WebContent/WEB-INF/lib に、mojarra-2.0.0-RC-binaryに含まれてる jsf-api.jar とjsf-impl.jar を入れる。
  • web.xml に、以下のような faces 関連のサーブレット定義を追加する。
    <servlet>
      <servlet-name>Faces Servlet</servlet-name>
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>/faces/*</url-pattern>
      <url-pattern>*.faces</url-pattern>
    </servlet-mapping>
    
  • WebContent 下に pages ディレクトリを作り、以下の内容の update.xhtml を作る
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" 
          xmlns:h="http://java.sun.com/jsf/html">
      <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
        <title>Ajax-ServerTime</title>
      </h:head>
      <h:body>
        <h:form id="form1" prependId="false">
          <h:outputScript name="jsf.js" library="javax.faces" target="head"/>
          <h:outputText id="textServerTime" value="#{serverTimeBean.time}"/>
          <br/>
          <h:commandButton id="update" value="update"
              onclick="jsf.ajax.request(this, event, {execute:'update', render: 'textServerTime'}); return false;"
              actionListener="#{serverTimeBean.update}"/>
        </h:form>
      </h:body>
    </html>
    
  • 適当なパッケージに以下のように ServerTimeBeanクラスを作る
    package net.xad.web.jsf20.trial1;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    import javax.faces.event.ActionEvent;
    import javax.faces.bean.ManagedBean;
    import javax.faces.bean.SessionScoped;
    
    @ManagedBean(name = "serverTimeBean")
    @SessionScoped
    public class ServerTimeBean {
       private String serverTime = "";
       public ServerTimeBean() {
          updateTime();
       }
       public String getTime() {
          return serverTime;
       }
       public void update(ActionEvent ae) {
          updateTime();
       }
       private void updateTime() {
          SimpleDateFormat format = new SimpleDateFormat("yy/MM/dd HH:mm:ss");
          serverTime = format.format(new Date());
       }
    }
  • Eclipse の Servers ビューから、[Add and Remove...]でTomcat サーバにプロジェクトを追加
■ 確かめる Servers ビューから、Tomcat サーバを起動する。確認には FireFox から FireBugを使ってみる。次のような URLを打ち込むと下図のような初期表示が得られる。 http://localhost:8080/trial1/pages/update.faces F12 で FireBug を開いて、[Net]プルダウンで Enabled に切り替えると、通信内容がモニタリングされるようになる。ここで update ボタンを押下すると、時刻表示が更新されると同時に、FireBug のペインにも一行追加される。これを展開する。 Post タブでリクエストの内容が表示される。 Response タブでは、返された XML が表示される。読みづらいので整形すると以下のようになる。
<?xml version="1.0" encoding="utf-8"?>
<partial-response>
  <changes>
    <update id="textServerTime">
      <![CDATA[<span id="textServerTime">09/10/29 11:26:33</span>]]>
    </update>
    <update id="javax.faces.ViewState">
      <![CDATA[5735020966984487302:3643923077241363467]]>
    </update>
  </changes>
</partial-response>
HTML タブで現在の DOMツリーを表示して、id=textServerTime の<span>要素を観察すると、レスポンスで返されたXML の小片を使って、HTML の小部分のみを更新しているらしいのが分かる。 後ついでに、デバッガで ServerTimeBean の呼ばれ方なども観察してみる。 ■ 課題 maven (m2eclipse) とWTPが上手く連携しないので、今回は WTP のみでやってみた。maven で作ったプロジェクトを Dynamic Web Project にする事も、Dynamic Web Project に maven の依存性管理を与えることも、どちらもできないことは無いけど、面倒な上に、その後の eclipse からの起動がすんなり行かなくて更に一手間二手間かかる。依存性管理はなるべく maven に全部任せたいけど、なかなかうまくいかない。

Spring 2.5/JSR-250

Spring 2.5 の JSR-250 対応APIを調べてみる。 具体的には、@Resource, @PostConstruct, @PreDestroy という三つのアノテーションで、以下のような、無味乾燥なコードで挙動を確かめてみたい。(ただしローカル前提とし、@Resource の JNDI LookUp は度外視する。)
package net.xad.spring25.trial1.jsr250_2;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;

@Service
public class ServiceA {
   @Resource
   private ServiceB serviceB;

   private boolean initialized = false;
   private boolean cleanedUp = false;

   @PostConstruct
   private void initialize() {
      initialized = true;
   }
   @PreDestroy
   private void cleanUp() {
      cleanedUp = true;
   }
   public boolean isInjectedCorrectly() {
      return null != serviceB;
   }
   public boolean isInitializedCalled() {
      return initialized;
   }
   public boolean isCleanUpCalled() {
      return cleanedUp;
   }
}
ソースの意味は以下
  • フィールド serviceB はリソースとしてインジェクトされる beanで、クラスServiceBは以下のような定義。単なる実験用なので、以下のような空実装で手を抜く。
    package net.xad.spring25.trial1.jsr250_2;
    import org.springframework.stereotype.Service;
    @Service public class ServiceB {}
  • フィールド initialized は、@PostConstruct が付いたメソッド initialize()が呼ばれた時、false から true になる。
  • フィールド cleanedUp は、@PreDestroy が付いたメソッド cleanUp() が呼ばれた時、false から true になる。
  • ※ serviceB に setter が無い事と、serviceB, initialize(), cleanUp()が private である事に注意。
この ServiceAクラスを以下のようなテストクラスで確かめてみる。
package net.xad.spring25.trial1.jsr250_2;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppTest {
   @Test
   public void testApp() {
      AbstractApplicationContext ctx = new ClassPathXmlApplicationContext(
         "classpath:application-context.xml");

      ServiceA serviceA = (ServiceA)ctx.getBean("serviceA");

      //ServiceB がちゃんとインジェクト済み
      assertTrue(serviceA.isInjectedCorrectly());
      //initialize() が呼ばれた
      assertTrue(serviceA.isInitializedCalled());
      //cleanUp() はまだ呼ばれてない
      assertFalse(serviceA.isCleanUpCalled());

      ctx.close();
      //cleanUp() が呼ばれた
      assertTrue(serviceA.isCleanUpCalled());
   }
}
プロジェクトは以下のように作る。
  • maven-archetype-quickstart でプロジェクト作成
  • 自動生成の不要ファイルを削除する
  • 上記ソースを、適当に作成
  • pom.xml の dependencies を以下のように編集
       <dependencies>
          <dependency>
             <groupId>org.springframework</groupId>
             <artifactId>spring</artifactId>
             <version>2.5.6</version>
          </dependency>
          <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>5.1.10</version>
          </dependency>
          <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <version>4.7</version>
          </dependency>
       </dependencies>
    
  • 以下のような application-context.xml を、src/main/resources などに追加。
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    
     <context:component-scan base-package="net.xad.spring25.trial1" />
    </beans>
ビルド・エラー等が無い事を確認して、AppTest.java上 の Alt+Shift+Tで実行。 上手くいったことを確認。めでたしめでたし。

2009年10月28日水曜日

Spring 2.5.6+Eclipse+Maven

この「Getting started with Maven and Spring」という記事を、ほんの少しアレンジしてやってみる。 元記事との相違点は以下のとおり。
  • コマンドラインではなく、Eclipse3.5 を使っている
  • Spring 2.5.5ではなく、2.5.6 を使っている
  • bean定義に DTDではなくXMLSchemaを使っている
  • パッケージ名とか固有名詞を適当に変えた
■ Maven Project 作成
  • archetype: maven-archetype-quickstart 1.0
  • group id: net.yasuabe.spring25.trial1
  • artifact id: hello-world-1
  • version: 0.0.1-SNAPSHOT
  • package: net.yasuabe.spring25.trial1.hello_world_1
※group id、artifact id は適当。versionはデフォルト、package は group id とartifact id から自動生成されるデフォルト。 ■ pomを編集
  • dependenciesに以下を追加
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring</artifactId>
      <version>2.5.6</version>
    </dependency>
    
  • PackageExplorer のMavenDependencies 下にspring とcommons-loggingが追加されるのを確認
■ App.java
  • 自動生成されている App.java のmain()を、以下の用に編集
      public static void main(String[] args) {
        BeanFactory factory = new XmlBeanFactory(
            new ClassPathResource("application-context.xml"));
        SayHello hello = (SayHello) factory.getBean("hello");
        hello.greet();
      }
    
  • 赤アンダーライン上の Ctrl+1 でimport を追加
■ SayHello.java
  • 以下のクラスを追加
    package net.yasuabe.spring25.trial1.hello_world_1;
    
    public class SayHello {
      private String name;
    
      public void setName(String name) {
        this.name = name;
      }
      public String getName() {
        return name;
      }
      public void greet() {
        System.out.println("Hello " + getName());
      }
    }
    
■ application-context.xml
  • [New Source Folder]でsrc/main/resourcesを作成
  • 以下のようにapplication-context.xmlを作成
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
     <bean id="hello" class="net.yasuabe.spring25.trial1.hello_world_1.SayHello">
      <property name="name" value="Pookey" />
     </bean>
    </beans>
    
■ 実行
  • App.java 上で Alt + Shift + jで実行、コンソールに"Hello Pookey"が出力されるのを確認
※注意 org.eclipse.wst.xsl.jaxp.debug.invoker.TransformationException が、実行時に出たら、ひとまず Eclipse の Preference からvalidation のXML、XSLあたりのチェックを外してみると、多分大丈夫。一旦、実行に成功すると、validation のチェックを戻しても動くようになってるはず。

A Chronology of Japanese History 100

前に作った年表暗記カードが80項目で、なんとなくキリが悪いので100項目に増やした。この年表→[リンク]が詳しすぎず粗すぎずちょうど良い感じ。

Japanese Chronological Table
{ "height":160,"width":400,"opacity":0.9,"answer-bg-color":"#FFEEDD","question-bg-color":"#FFEEDD","answer-color":"#006622","question-color":"#002266","caption":"暗記帳 アルファ版"}
Start of human activity on the Japanese archipelago.
ca.100,000B.C.
Spread of Late Paleolithic Culture.
ca.30,000B.C.
First use of pottery on Japanese archipelago.
ca.10,000B.C.
First production of clay images.
ca.7,000B.C.
First large Jomon communities.
ca.3,000B.C.
Establishment of Yayoi Culture in northern Kyushu. Start of rice cultivation.
ca.300B.C.
King Na of Wa (Japan) sends tribute to Eastern Han dynasty and is bestowed gift of a gold seal.
57 A.D.
First construction of large graves with burial mounds in western Japan.
ca.200
Queen Himiko of Yamatai in Wa sends emissaries to Wei in China.
239
Keyhole-shaped burial mounds are constructed in many parts of western Japan.
ca.300
Wa sends troops to the Korean peninsula (monument of Haotai Wang).
Religious rites begin around this time on Okinoshima off the coast of Kyushu.
391
King of Wa starts in this year to send tribute to the Southern Court in China.
421
Forging of the iron swords later unearthed at the Inariyama Burial Mound in Saitama Prefecture.
471
Buddhism introduced from Paekche on the Korean peninsula (538 according to one theory).
552
King of Wa sends emissaries to the Sui Dynasty in China.
600
Prince Shotoku creates the Seventeen-Article Constitution.
604
First emissaries sent to the Tang Dynasty.
630
Taika Reforms.
645
Japanese troops defeated by troops of Tang and Silla on the Korean peninsula. Defenses of western Japan are strengthened.
663
Completion of the Taiho Ritsuryo Code. Establishment of a state based on the Ritsuryo laws.
701
Capital is moved to Heijokyo (Nara).
710
Construction of Tagajo Castle in Tohoku region (monument of Tagajo) .
724
Completion of the Great Buddha at Todaiji Temple.
752
Belongings of Emperor Shomu moved to Todaiji. Construction of Shosoin.
758
Hyakumanto Darani (Dharani of the Million Towers) is completed.
770
Transfer of the capital to Heiankyo (Kyoto).
794
Practice of sending emissaries to China is discontinued.
894
Fujiwara Michinaga takes hold of power. Flourishing of the Fujiwara family.
995
Murasaki Shikibu completes The Tale of Genji.
1005
Sutra mounds created in great numbers out of beliefs that Mappo (The Age of Decline) has started.
1052
Minamoto Yoritomo establishes his headquarters in Kamakura.
1180
Yoritomo becomes Seii tai shogun ("Barbarian Conquering General") and establishes the Kamakura shogunate.
1192
Monk Shinran founds the Jodo Shinshu Sect (Ikko Sect).
1224
Monk Ippen founds the Ji Sect.
1275
Collapse of Kamakura shogunate. Kamakura enveloped in the flames of war.
1333
Ashikaga Takauji becomes Seii tai shogun and founds the Muromachi shogunate.
1338
Japanese pirates begin raid along China's coastline.
1350
Ashikaga Yoshimitsu restores relations with Ming China. Ashikaga is recognized by the Ming court as the "King of Japan".
1401
Sho Hashi unites all of Okinawa and becomes the Ryukyu King.
1422
Do ikki uprising develops in Kyoto and results in the Tokusei edict. Tea ceremony begins to flourish at this time.
1441
Onin War. Kyoto is enveloped in flames of war.
1467
Ikko ikki breaks out in Kaga and assumes control of the entire Kaga Province.
1488
Asakura Norikage quells the Ikko ikki.
1531
Date Tanemune enacts the Jinkaishu which established domain. Daimyo in different areas begin at this time to enact their own legal systems.
1536
Portuguese bring guns to Tanegashima.
1543
Francis Xavier arrives in Japan to begin missionary activities.
1549
Fall of the Asakura daimyo family in Ichijodani, Echizen Province.
1573
Toyotomi Hideyoshi unites all of Japan.
Start of printing of Christian books in Romanized Japanese.
1590
Start of the system of Shuinsen or "red seal ships". Hideyoshi sends troops to Korea.
1592
Tokugawa Ieyasu becomes Seii tai shogun, establishes the Edo shogunate.
1603
Ieyasu has bronze movable type forged to print the Suruga edition.
1607
The hishigaki kaisen (lozenge-fenced cargo ships) begin to sail regularly between Edo and Osaka.
1619
Shogunate forbids Japanese to travel overseas.
Start of the sankin kotai system which required daimyo to reside alternate years in Edo.
Construction of national roads.
1635
Entry of Portuguese ships forbidden. Start of sakoku, a period in which Japan was closed off to the outside world.
1639
Dutch Trading Mission is moved to Dejima in Nagasaki which becomes the only port in Japan where foreign trade is allowed.
1641
Promulgation of the Keian no ofuregaki, a document outlining the duties and conduct of the farmers.
1649
Great Edo Fire. Following this, large spaces are created in the city to prevent further fires.
1657
Ainu rebellion in Ezochi (Hokkaido).
1669
Kawamura Zuiken opens eastern sea route. Western sea route is opened in following year.
1671
Start of Genroku Period. Development of Edo culture including Kabuki and Bunraku.
1688
Kyoho Famine. Rice stores broken into as prices on rice soar.
1732
Russian ships land in Ezochi, request trade.
1778
Ino Tadataka produces first accurate maps of the Japan's coastline.
1821
Franz von Siebold, a German physician of the Dutch Trading Mission, is banished from Japan for bringing maps of Japan out of the country.
1828
Admiral Perry arrives in Uraga and demands that Japan opens its ports.
1853
Japan concludes friendship treaties with the United States, Britain, Russia, France and the Netherlands. The ports of Hakodate, Shimoda and Nagasaki are opened to foreign trade.
1854
Meiji Restoration. Edo's name is changed to Tokyo ("Eastern Capital") .
1868
The Ryuku Islands become Okinawa Prefecture.
1879
Promulgation of the Constitution of the Empire of Japan.
1889
Outbreak of Russo-Japanese War.
1904
Korea becomes a Japanese colony.
1910
Japan presents China with a set of 21 demands for expanding its rights in China.
1915
Start of a financial panic. Government declares moratorium on gold standard.
1927
The so-called Liutaochu Incident is used as a pretext for the start of military maneuvers in northeast China. Start of invasion of China.
1931
Start of Pacific War.
1941
Promulgation of the Constitution of Japan. Agrarian reforms.
1946
Signing of the Treaty of San Francisco.
1951
Enactment of the Basic Agricultural Law.
1961
Start of the New Tokaido Railway (Shinkansen). Beginning of free economic system.
1964
Outbreak of itaiitai sickness in Toyama Prefecture (first case of pollution related diseases due to industrial wastes).
1967
Unified by Yamato regime.
ca. 350
The Jinshin war broke out.
672
Kojiki was edited.
712
Kukai returned to Japan.
806
Insei by retired emperor Shirakawa started.
1086
Taira no Kiyomori was appointed Daijo Daijin.
1167
The JMei Code (Goseibai Shikimoku) is issued.
1232
Yoshimitsu builds his retirement retreat at Kinkakuji.
1398
Yoshimasa begins construction of Ginkakuji.
1489
Battle of Okehazama
1560
Battle of Nagashino
1575
the Siege of Osaka(Winter Campaign).
1614
Incident of the 47 Ronin.
1701
The Kyoho reforms
1716
Kaitai Shinsho was published
1774
Mamiya Rinzo's exploration of Sakhalin
1808
Sakurada-mon Incident
1860
Seinan Rebellion
1877
enthronement of Hirohito.
1926
2-2-6 incident
1936

wikipediaも併用して20項目を追加したけど、英語になった日本史キーワードは、やはりちょっと面白い。大阪冬の陣が Seige of Osaka (Winter campaign) になるんだなあ。Siege はウィーン包囲(Siege of Vienna)の seige だから解るけど、campaign にそんな用法があったんだ。あと「Battle of Okehazama」なんて言うのが、何故かちょっと格好いい。ってミーハーか。

2009年10月27日火曜日

FP2級合格

今日、ファイナンシャル・プランナー技能士試験の合格発表があった。自己採点でギリギリ合格を予想していたが、どうやら受かっていた。

以前から思ってるけど、やっぱりオブジェクト指向技術者こそ、業務要件の取りまとめから仕様策定の辺りに、もっと積極的に関わって行かないと駄目なんじゃなかろうか。自分にも言えるけど、現場のかなり多くのオブジェクト指向技術者は、フレームワークまわりの作業ばかりで、実は意外とビジネスロジックは担当していなかったりする。

既存のフレームワークや低水準のAPIをラッピングして、ビジネスロジックを分離する仕組みをオブジェクト指向技術者が作る。そうやって切り離されたビジネスロジックを、「業務SE」が処理の流れをズラズラを書き並べた設計書に基づいて、下位プログラマがコーディングする。こんな風な作業分担で書かれたビジネスロジックは、得てして、数百行~数千行の巨大な「関数」になってしまいがち。もちろん凄いスパゲティコードで重複コードありまくりみたいな。

やっぱり、オブジェクト指向技術者が業務分析から OOA でやるべきなんだろう。そもそも、そういう現実世界の事象をモデリングする事こそが、DI やら O/R マッピング以上に、本来のオブジェクト指向の醍醐味ではなかったか。

というわけで、今後は、業務/ドメインに関わる比重を増やして、ビジネスロジック(ドメインモデル)の領域と実装技術の領域の橋渡しになるような仕事をしたい。ただし、その前にまずはそういったポジションに着かなければならないけど、なんとかこの資格がちょっとは役に立ってくれたらいいけどなあ・・・

2009年10月24日土曜日

jBPM4 の永続化

jBPM 4 の永続化を軽く調べてみたので、以下レポート。

■ やり方

start→first→second→end というプロセスを始動してデバッガでステップ実行しながら、データベースの中身をのぞいてみる事にする。

■ テストアプリ作成

こんな風にテストアプリを作った。

  1. まず maven-archetype-quickstart を使ってMaven Projectを作成。
  2. pom.xml の dependencies を以下のようにする
    <dependencies>
      <dependency>
        <groupId>org.jbpm.jbpm4</groupId>
        <artifactId>jbpm-jboss4</artifactId>
        <version>4.1</version>
      </dependency>
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.10</version>
      </dependency>
     </dependencies>
    
    リポジトリURLはhttp://repository.jboss.com/maven2
  3. 新規ソースフォルダ src/main/resource を作成し、ファイル test.jpdl.xml を置く。これがプロセス定義で、以下のような内容。
    <?xml version="1.0" encoding="UTF-8"?>
    <process name="test_sequence" xmlns="http://jbpm.org/4.0/jpdl">
      <start>
        <transition to="first" />
      </start>
      <state name="first">
        <transition to="second" />
      </state>
      <state name="second">
        <transition to="end" />
      </state>
      <end name="end"/>
    </process>
    
  4. 同フォルダに、ファイルjbpm.cfg.xml を置く。
    <?xml version="1.0" encoding="UTF-8"?>
    <jbpm-configuration>
      <import resource="jbpm.default.cfg.xml" />
      <import resource="jbpm.jpdl.cfg.xml" />
      <import resource="jbpm.tx.hibernate.cfg.xml" />
      <import resource="jbpm.identity.cfg.xml" />
    </jbpm-configuration>
    
  5. 同フォルダに、ファイル jbpm.hibernate.cfg.xml ファイルを置く。内容は以下のような感じで適当に。
    <?xml version="1.0" encoding="utf-8"?>
    
    <!DOCTYPE hibernate-configuration PUBLIC
              "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
              "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
    
    <hibernate-configuration>
     <session-factory>
      <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
      <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
      <property name="hibernate.connection.url">jdbc:mysql://localhost/jbpm</property>
      <property name="hibernate.connection.username"></property>
      <property name="hibernate.connection.password"></property>
      <property name="hibernate.hbm2ddl.auto">create</property>
      <property name="hibernate.format_sql">true</property>
    
      <mapping resource="jbpm.repository.hbm.xml" />
      <mapping resource="jbpm.execution.hbm.xml" />
      <mapping resource="jbpm.history.hbm.xml" />
      <mapping resource="jbpm.task.hbm.xml" />
      <mapping resource="jbpm.identity.hbm.xml" />
    
     </session-factory>
    </hibernate-configuration>
    
  6. 同フォルダに、ファイルlog4j.properties ファイルを置く。内容は適当。
    # Set root logger level to DEBUG and its only appender to CONSOLE.
    log4j.rootLogger=DEBUG, CONSOLE
    
    # CONSOLE
    log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
    log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
    log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %C{1} : %m%n
    
    # LIMIT CATEGORIES
    log4j.logger.org.jbpm=DEBUG
    log4j.logger.org.hibernate=INFO
  7. 適当にクラスを作って以下のようにmain() を書く。
    package net.xad.jbpm.trial2_db;
    
    import org.jbpm.api.Configuration;
    import org.jbpm.api.Execution;
    import org.jbpm.api.ExecutionService;
    import org.jbpm.api.ProcessEngine;
    import org.jbpm.api.ProcessInstance;
    import org.jbpm.api.RepositoryService;
    
    public class Test {
        public static void main(String[] args) {
          ProcessEngine engine = Configuration.getProcessEngine();
          RepositoryService repositoryService = engine.get(RepositoryService.class);
          ExecutionService executionService = engine.getExecutionService();
    
          repositoryService.createDeployment()
            .addResourceFromClasspath("test.jpdl.xml").deploy();
      
    /*1*/ ProcessInstance instance = executionService
              .startProcessInstanceByKey("test_sequence");
    
    /*2*/ Execution first = instance.findActiveExecutionIn("first");
          instance = executionService.signalExecutionById(first.getId());
    
    /*3*/ Execution second = instance.findActiveExecutionIn("second");
          instance = executionService.signalExecutionById(second.getId());
        }
    }

■ 実行してみる

main() の 1~3 の行に、ブレークポイントを設定してデバッグ実行。

(1)で停止した時点

test.jpdl.xml に記述されたプロセス定義がデータベースに登録された状態。

mysql> select * from jbpm4_deployment;
+-------+-------+------------+--------+
| DBID_ | NAME_ | TIMESTAMP_ | STATE_ |
+-------+-------+------------+--------+
|     1 | NULL  |          0 | active |
+-------+-------+------------+--------+
1 row in set (0.00 sec)

mysql> select * from jbpm4_deployprop;
+-------+-------------+---------------+-----------+-----------------+----------+
| DBID_ | DEPLOYMENT_ | OBJNAME_      | KEY_      | STRINGVAL_      | LONGVAL_ |
+-------+-------------+---------------+-----------+-----------------+----------+
|     1 |           1 | test_sequence | pdkey     | test_sequence   |     NULL |
|     2 |           1 | test_sequence | pdid      | test_sequence-1 |     NULL |
|     3 |           1 | test_sequence | pdversion | NULL            |        1 |
+-------+-------------+---------------+-----------+-----------------+----------+
3 rows in set (0.00 sec)
ルールとその属性が DBに登録されているのがわかる。省略するが、jbpm4_lob をみると プロセス定義がXMLのまま登録されているのがわかる。 ここから先で見ていくテーブル、jbpm4_hist_procinst と jbpm4_hist_actinst はまだ空。 ちなみにデータベースが空の状態で実行しても、hibernate が勝手にテーブルを作ってくれる。

(2)で停止した時点

プロセスを開始すると startノードから勝手に最初の state である"first"に進み、そこでシグナルを待つ。

mysql> select START_, END_, STATE_ from jbpm4_hist_procinst;
+---------------------+------+--------+
| START_              | END_ | STATE_ |
+---------------------+------+--------+
| 2009-10-24 21:53:52 | NULL | active |
+---------------------+------+--------+
1 row in set (0.00 sec)

mysql> select ACTIVITY_NAME_, START_, END_ from jbpm4_hist_actinst;
+----------------+---------------------+------+
| ACTIVITY_NAME_ | START_              | END_ |
+----------------+---------------------+------+
| first          | 2009-10-24 21:53:52 | NULL |
+----------------+---------------------+------+
1 row in set (0.00 sec)

(3)で停止した時点

first からsecond に進んで、jbpm4_hist_actinst が以下のように変化する。jbpm4_hist_procinst は無変化。

mysql> select ACTIVITY_NAME_, START_, END_ from jbpm4_hist_actinst;
+----------------+---------------------+---------------------+
| ACTIVITY_NAME_ | START_              | END_                |
+----------------+---------------------+---------------------+
| first          | 2009-10-24 21:53:52 | 2009-10-24 21:55:19 |
| second         | 2009-10-24 21:55:19 | NULL                |
+----------------+---------------------+---------------------+
2 rows in set (0.00 sec)

(3) から継続した後

second から end に進んで、プロセス終了。テーブルは以下のように更新される。

mysql> select START_, END_, STATE_ from jbpm4_hist_procinst;
+---------------------+---------------------+--------+
| START_              | END_                | STATE_ |
+---------------------+---------------------+--------+
| 2009-10-24 21:53:52 | 2009-10-24 21:55:41 | ended  |
+---------------------+---------------------+--------+
1 row in set (0.00 sec)

mysql> select ACTIVITY_NAME_, START_, END_ from jbpm4_hist_actinst;
+----------------+---------------------+---------------------+
| ACTIVITY_NAME_ | START_              | END_                |
+----------------+---------------------+---------------------+
| first          | 2009-10-24 21:53:52 | 2009-10-24 21:55:19 |
| second         | 2009-10-24 21:55:19 | 2009-10-24 21:55:41 |
+----------------+---------------------+---------------------+
2 rows in set (0.00 sec)

2009年10月23日金曜日

FUSE ESB 3.4 で Drools を使ってみる

最初 FUSE ESB 4.1 でやってみたけど、servicemix-drools コンポーネントを含むサービスアセンブリを osgi:install すると、どうしても osgi:start でエラーが発生する。Drools がルールを構築しようとして NullPointerException を発生させているらしい。

同じ目にあった人が FUSE のコミュニティで質問しているが、どうやら解決には至らなかった模様(リンク)。

リモートデバッガを引っ掛けて見てみると、直接的には Drools の dialect が認識されていないのが原因のようだけど、要するにクラスローダが正しく jar に関連付けられていないらしい。だから例えば、dialect関連のプロパティを-D オプションなんかで無理やり認識させたとしても、次はその dialect に必要なクラスがロードできないと言う事になる。(細かく言うと、jar は drools-compiler-4.0.7.jar、dialect が記述されているファイルは、その jar 中の drools.default.packagebuilder.conf、必要なクラスはJavaDialectConfigurationなど)

その後もしばらく、無理やり ServiceMix の lib に drools 関連の jar を追加してみるなどいろいろ悪あがきしたけど、LinkageError が出たりしてなんだかグタグタになり始め、面倒くさいから中断。実は、FUSE ESB 4.x はまだ incubation フェーズということで、発展途上という風にアナウンスされていたりする。今後に期待。

ということで mature フェーズのFUSE 3.4 を使ってみる事にした。せっかく OSGi 環境が好きになりかけていたんだけどなあ…

■ 概要 以下のように読みこんだファイルをルールに従って仕分けさせてみる
  • file サービス は、フォルダ poller/に xml ファイルが置かれるのを、poller エンドポイントで監視する。
  • file サービス、は xml ファイルが置かれるとこれを読み込み、droolsサービスの dispatch エンドポイント に送る。
  • drools サービス は、<purchase>/<price> 要素が 10000未満ならば、file サービスの sender_low エンドポイントにルーティングし、10000以上ならば file サービスの sender_high エンドポイントにルーティングする。
  • file サービス は、sender_low エンドポイントで受け取ったメッセージをフォルダ low/に保存し、sender_high エンドポイントで受け取ったメッセージはフォルダ high/に置く。
  • file サービス は、poller_low エンドポイントで読み込んだ xml ファイルをフォルダから削除する。
上記動作を、以下のようなモジュール構成で実装してみる
  • trial1-file-su:file サービスを定義するサービスユニット
  • trial1-drools-su:drools サービスを定義するサービスユニット
  • trial1-sa:上の二つのサービスユニットを含むサービスアセンブリ
環境は以下のとおり
  • Windows XP SP3
  • eclipse 3.5
  • FUSE ESB 3.4.0.4
  • アーキタイプのバージョンは基本的に最新版
以下、各モジュールのレシピ。

■ trial1-file-su
  • servicemix-file-poller-service-unit アーキタイプで新規プロジェクト作成
  • xbeans.xml を以下のように編集
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:file="http://servicemix.apache.org/file/1.0"
           xmlns:trial1="urn:net:yasuabe:fuse3"
           xmlns="http://www.springframework.org/schema/beans">
    
      <file:poller service="trial1:file"
                  endpoint="poller"
                  targetService="trial1:drools"
                  targetEndpoint="dispatch"
                  file="file:///c:/devel/tmp/200910fuse/xad/poller" />
      <file:sender service="trial1:file"
                  endpoint="sender_high"
                  directory="file:///c:/devel/tmp/200910fuse/xad/high" />
      <file:sender service="trial1:file"
                  endpoint="sender_low"
                  directory="file:///c:/devel/tmp/200910fuse/xad/low" />
    </beans>
    
■ trial1-drools-su
  • servicemix-drools-service-unit アーキタイプで新規プロジェクト作成
  • xbeans.xmlを以下のように編集
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:drools="http://servicemix.apache.org/drools/1.0"
           xmlns:trial1="urn:net:yasuabe:fuse3"
           xmlns="http://www.springframework.org/schema/beans">
    
      <drools:endpoint service="trial1:drools"
                       endpoint="dispatch"
                       ruleBaseResource="classpath:router.drl"/>
    </beans>
    
  • router.drl を以下のように編集
    package org.apache.servicemix.drools
     
    import org.apache.servicemix.drools.model.Exchange;
    
    global org.apache.servicemix.drools.model.JbiHelper jbi;
     
    rule "cheap"
     when
      Exchange(status == Exchange.ACTIVE, inMsg : in != null )
      eval(inMsg.xpath("/purchase/price<10000"))
     then
      jbi.route("endpoint:urn:net:yasuabe:fuse3:file:sender_low");
      jbi.getLogger().info("cheap");
    end
    
    rule "expensive"
     when
      Exchange(status == Exchange.ACTIVE, inMsg : in != null )
      eval(inMsg.xpath("/purchase/price>=10000"))
     then
      jbi.route("endpoint:urn:net:yasuabe:fuse3:file:sender_high");
      jbi.getLogger().info("expensive");
    end
    
■ trial1-sa
  • servicemix-service-assembly アーキタイプで新規プロジェクト作成
  • pom.xmlを編集して以下のような dependencies にする
  •   <dependencies>    
        <dependency>
          <groupId>net.yasuabe.fuse3</groupId>
          <artifactId>trial1-drools-su</artifactId>
          <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
          <groupId>net.yasuabe.fuse3</groupId>
          <artifactId>trial1-file-su</artifactId>
          <version>0.0.1-SNAPSHOT</version>
        </dependency>
      </dependencies>
    
■ ビルドして動かしてみる
  • ビルドは Eclipse 上で [mvn install]した
  • デプロイは maven の jbi:projectDeploy ゴールを試したが、この質問(→リンク)と同様に Repository が見つからない現象が再現したので、意地を張るのは止めて hotdeploy を使ってデプロイすることにした。
  • で、次のように確認
    • 以下のようなファイルを poller に置いて、low に移動することを確認した。
      <?xml version="1.0" encoding="UTF-8"?>
      <message>
       <amount>9999</amount>
      </message>
      
    • 以下のようなファイルを poller に置いて、high に移動することを確認した。
      <?xml version="1.0" encoding="UTF-8"?>
      <message>
       <amount>10000</amount>
      </message>
      
    • どちらでもログが出力されることを確認した。
■ その他メモ
上記の試行はfile コンポーネントとdrools コンポーネントだけのものだけど、他にもいろいろな組み合わせを試したので、困った事・分かった事を書いておく。

・servicemix-drools コンポーネントの挙動が分かりにくい
Camel のルーティングの from() にタイマやファイル監視を指定、to() に Droolsサービスを指定するような設定で、Drools から返ってこないまま止まっている感じになる事があった。この場合、例えば タイマなら3秒に一回メッセージが来るはずが、二つ目以降のメッセージが流れて来ず、ファイル監視なら処理の終わりで削除されるはずのファイルがロックされたまま消されずに残っているといった状況。jbiHelper の answer()やroute()、あるいは fault()のようなメソッドを呼ばないと返らないのかも。

・JMS が大量にソケットを消費して、しまいにポート 61616(JMSのコネクション)につながらなくなる
(java.net.BindException) ここでレポートされている(→リンク)のと同様の現象。
試しに Spring で最大コネクション数1のコネクションプールを指定してみると、キューイングしたところで、最初のキューイングで JMS が固まっている。という事はプール不使用の場合は新たなコネクションを作り続けて限界に達しているという事か。だけど、そもそも何故そんなに何百回も接続しようとするのか分からない。
ちなみに上のリンクでは、結局 ActiveMQ と Camel を subversion のトランクから落としてリビルドしたようだけど、面倒だからパス。と言うわけで、上述のレシピのように、JMS の代わりにfile:sender を用いることにした。
いろいろ不可解な現象があったり情報が少なかったり、面倒くさいことがちょっと多かったけど、まあ手間がかかった分わかった事も多いので、良しとしよう。

終わり

2009年10月21日水曜日

ServiceMix Tutorial を FUSE ESB 4.1で

ServiceMix のサイトにあるチュートリアルと同じ事を、Eclipse(3.5.0) と FUSE ESB(ServiceMix4)を用いてやってみる。(ServiceMix のバージョンは、4.1.0.2-fuse)

■ 概要
「あるディレクトリを監視して、ファイルが置かれたら別のディレクトリに移動する。」という、極々シンプルな仕様。流れとしては、まずservice unit、service assembly の順に作成し、これをServiceMixにインストールして動作確認するだけ。至って簡単。

■ service unit
ファイルの監視と移動にはservicemix-file コンポーネントを用いる。servicemix-file の動作は、service unit で定義するが、これを以下のように eclipse プロジェクトとして作成する。
  • [New]>[Project...]で [Maven Project]を選択
  • Archetype の選択で "Nexus Indexer"カタログから"servicemix-service-unit"を指定 (無ければ Maven Indexビューで"http://repo.fusesource.com/maven2-snapshot" か"http://repo.fusesource.com/maven2"を追加するとリストアップされるようになる。)
  • Artifact Id 等は説明の都合上以下のようにする。
    • Group Id:org.apache.servicemix.tutorial
    • Artifact Id:tutorial-file-su
    • Version:0.0.1-SNAPSHOT
    • Package:org.apache.servicemix.tutorial.tutorial_file_su
次に以下のようにpom.xmlを編集する。
  • project/dependenciesに、以下のように servicemix-file への dependency を追加する。(JUnitのdependency はいらないので消す。)
    <dependency>
        <groupId>org.apache.servicemix</groupId>
        <artifactId>servicemix-file</artifactId>
        <version>2009.03-fuse-SNAPSHOT</version>
    </dependency>
    
  • project/name要素 を "Tutorial :: File SU"に変更

また以下のように xbean.xml を作成する
  • src/main/resources直下に xbean.xml を作成する
  • 内容は以下のように記述する
    <beans
            xmlns:file="http://servicemix.apache.org/file/1.0"
            xmlns:tut="urn:servicemix:tutorial"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <file:sender service="tut:file" endpoint="sender"
             directory="file:///c:/tmp/gert/sender"/>
        <file:poller service="tut:file" endpoint="poller"
             file="file:///c:/tmp/gert/poller" targetService="tut:file"
             targetEndpoint="sender"/>
    </beans>
  • sender@directory にはコピー先のディレクトリパス、poller@file には監視対象のディレクトリパスを記述する
  • Windows 環境でドライブレターを含む場合、上記のように"file:///c:/tmp/poller/"みたいに書く
  • servicemix-file の細かいことは、元サイトを参照
これを次のようにビルドする。
  • [Run As]>[Maven install]
  • 成果物がローカルリポジトリにインストールされるが、確認するには eclipse のMaven Indexビューで local リポジトリを見ればいい。
■ service assembly
service unit を使うには service assembly に組み込んで ServiceMix に配備するが、service assembly も 以下のように eclipse プロジェクトとして作成する。
  • [New]>[Project...]で [Maven Project]を選択
  • Archetype の選択で "Nexus Indexer"カタログから"servicemix-service-assembly"を指定
  • Artifact Id 等は説明の都合上以下のようにする。
    • Group Id:org.apache.servicemix.tutorial
    • Artifact Id:tutorial-sa
    • Version:0.0.1-SNAPSHOT
    • Package:org.apache.servicemix.tutorial.tutorial_sa
次のように pom.xml を編集。
  • project/dependencies に、上で作成・インストールした service unit を指定する。
    <dependencies>
       <dependency>
         <groupId>org.apache.servicemix.tutorial</groupId>
         <artifactId>tutorial-file-su</artifactId>
         <version>0.0.1-SNAPSHOT</version>
       </dependency>
     </dependencies>
    
  • project/name を "Tutorial :: SA"に変更
これを [Run As]>[Maven install] でビルドする。確認は service unit と同様

■ インストールと動作確認
  • ServiceMix コンソールから次のコマンドでインストール。-s オプションがあるので自動的にスタートする。
    osgi/install -s mvn:org.apache.servicemix.tutorial/tutorial-sa/0.0.1-SNAPSHOT/zip
  • osgi/list で、最終行に以下のような行があることを確認。
    [ 175] [Active     ] [       ] [   60] mvn:org.apache.servicemix/tutorial-sa/0.0.1-SNAPSHOT/zip
  • nmr/list で以下の行が含まれている事を確認
    {urn:servicemix:tutorial}file:sender
    {urn:servicemix:tutorial}file:poller
  • jbi/list で以下の行が含まれている事を確認
    [Started ] [tutorial-sa                     ]     FUSE TRIAL1 :: SA
  • c:/tmp/gert/poller に適当なファイルを置いて、自動的にc:/tmp/gert/sender に移動される事を確認
終わり。

2009年10月20日火曜日

観察:Getting Started with FUSE 4 (2)

前回前々回に続いて、FUSE の webinar 「Getting Started with FUSE 4」の復習。

payment-service-bc

■beans.xml の編集
cxfbc:consumer 要素を使っている。詳細は[ここ] 他は、payment-service-se の beans.xml と同様。 ちなみに、webinar では soapUI を使って SOAPメッセージを送っているが、 payment-service-se 中の PaymentServiceクラス(自動生成されたやつ)を使えば、以下のようなコードでWebサービスの動作確認ができる。
public static void main(String[] args) {
    ObjectFactory factory = new ObjectFactory();

    Transfer payload = factory.createTransfer();
    payload.setAmount("123");
    payload.setFrom("Me");
    payload.setTo("You");
   
    PaymentService service = new PaymentService();
    Payment endpoint = service.getSoap();
    Response response = endpoint.transferFunds(payload);

    System.out.println(response.getReply());
}

payment-service-batch

■ プロジェクトを生成した直後の状態
servicemix-osgi-camel-archetype を使って、以下のようなファイルを含むプロジェクトの雛形を作成。
  • src/main/java/com/example/fuse/payment_service_batch2/MyTransform.java route の中で使用する Javaクラス のスケルトン。使わないのでsrc/main/javaごと削除する。
  • src/main/resources/META-INF/spring/camel-context.xml camel のルーティング。後で編集する
  • pom.xml maven の設定ファイル。後で編集する。

■ camel-context の編集
camelContext 要素内で、route が二つ定義されている。
  • まず一つめ
    • FUSEホーム直下の tmp/file-in フォルダにおかれたファイルを読み込む
    • それを文字列にする
    • 読んだ文字列をログに出力する
    • 読んだ文字列を XMLとして解釈し、XPATH式 transfers/transfer の要素を、ログ出力すると同時に、JBIの
    • paymentService:endpointに送る
  • 二つめ
    • 2秒ごとのタイマーイベントに応じて
    • CDATA で定義した内容をメッセージボディとし、
    • それをログに出力し、
    • ファイル名を transfer.xml とするよう宣言し、
    • FUSEホーム直下の tmp/file-in に保存する

==== ====
だいたい webinar の内容はわかった。

SeriviceMix 3.X までは、仕事でも私用でも以前から若干経験があったけど、ServiceMix4で OSGI ベースになってから、使い方が大幅に変わったり、以前の資料の大半が使えなくなったりしてちょっと敬遠していたけど、まあ、なんとかなりそう。

むしろ昔より使いやすいかもしれない。提供されている maven アーキタイプに逆らわずに、自然な流れをキープすることに留意すれば、わりと良い生産性が見込めるかもしれない。 ただやはり、情報や資料が少なすぎるのは、リスクになるかも。

2009年10月19日月曜日

観察:Getting Started with FUSE 4 (1)

この間の続き。[前回]

payment-service-se、payment-service-bc、payment-service-batch と、webinarで作成する三つのプロジェクトのうち、まず最初の payment-service-se を少し詳しく見てみる。流れは README.txtに書いてある手順に従う。

プロジェクトを生成した直後の状態
M2 のアーキタイプ servicemix-osgi-cxf-wsdl-first-archetype が、Maven 2 の規約に従ったディレクトリ構成で、以下のようなファイルを自動生成している。
  • src/main/resources/META-INF/spring/beans.xml
    後で内容をリプレスする
  • src/main/resources/wsdl/person.wsdl
    別のwsdlを使うので削除する
  • pom.xml
    maven の設定ファイル。後で編集する。


paymentService.wsdl のインポート
WSDL定義の内容は、以下のようなもの
  • サービス PaymentServiceはポートsoap を持つ
  • ポート soap のポートタイプ は Paymentである。
  • ポートタイプ Payment は、操作 transferFundsを持つ。
  • 操作 transferFunds の入力はメッセージ TransferRequest であり、出力はメッセージTransferResponseである。
  • メッセージTransferRequest は XSD型 transferに、メッセージTransferResponseは、XSD型 responseに関連付けられている。


■pom.xmlの編集
  • プロジェクト名は OSGIバンドルの名前としても用いられ、ServiceMix 上でもこの名前で表示される

  • Dependencies で設定するモジュールは、Payment サービスの実装クラス PaymentImpl で使うもの
  • cxf-codegen-plugin は wsdl からコードを生成するために用いる。wsdlファイル名とソース生成先を変更。
  • maven-bundle-plugin は OSGI バンドルを作るmaven プラグイン。詳細はここ


■maven install
maven install を実行してソースを自動生成する。webinar では「install」だが「cxf-codegen:wsdl2java」ゴールでも可。以下のように pom.xml で指定したフォルダtarget/generated/main/java下に、ソースファイルが生成される。
  • com.example.fuse.payment_service
    Payment…paymentService.wsdl に書かれた wsdl:portType 定義に対応するインターフェイス
    PaymentService…クライアントスタブ
  • com.example.fuse.payment_service.types
    Transfer…paymentService.wsdl 中のtransfer型 に対応するJavaクラス
    Response…paymentService.wsdl 中のresponse型 に対応するJavaクラス
    ObjectFactory・・・Transfer とResponseのファクトリクラス
    package-info・・・パッケージとXML名前空間を対応付ける


■Paymentインターフェイスの実装
PaymentImplクラス で Payment インターフェイスと InitializingBean を実装する。
  • transferFunds()メソッドで、入力メッセージをログ出力し「OK」を出力メッセージに設定する
  • SpringのInitializingBeanでもあるので、afterPropertiesSet()に確認のためのログ出力コードを書いておく
  • setContext()メソッドは Spring からインジェクタメソッドとして呼ばれる


■spring のbean.xml の編集
  1. エンドポイントでの実際の処理を行うクラスとして PaymentImpl クラスを指定する。
  2. EndpointExporter に PaymentImpl を登録させる
  3. 他の編集箇所は、上記1,2のためのリソースや名前空間の準備。


プロジェクトpayment-service-seは、以上のような感じ。

追試:Getting Started with FUSE 4

FUSE という ServiceMix のバンドルを試してみたので、以下にレポート。

ネタはこのサイト(→リンク)で配信されている、Getting Started with FUSE 4 という webinar セッション。動画はローカルに保存した方が見やすいので、指定のサンプルファイルと共にローカルに落としておく。

基本的には、ダウンロードしたファイル群に含まれる README.txt に手順が一通り書いてあり、それだけを見ながらできない事もないが、一応、Webinar動画の方も見ておいた方がいい。

ツール・ライブラリ等について、敢えて webinar と少しだけ違うバージョンのものを、使えるところは使ってみたので、以下に書いておく。
FUSE4.1.0.2
Eclipse3.5.0
soapUI3.0.1
servicemix-osgi-cxf-wsdl-first-archetype2009.03-fuse-SNAPSHOT
jbi-api-1.01.3.0.2-fuse
commons-logging1.1.1
spring-beans2.5.6
cxf-codegen-plugin2.2.2.2-fuse
camel-version1.6.1.0-fuse

いくつか問題が発生したが、以下のようにして解決。
■ settings.xml が見つからなくて、maven index が更新されない

Window>Preference>>Maven>Installations>User Settingsで正しいパスを指定

■ Javaコードで、コンパイルエラー(Access restriction)
コンパイラセッティングを変更する→参考リンク

■ インストールした BC/SE の start で失敗
pomを以下のように変更
× org.apache.cxf.cxf-bundle
org.apache.cxf.bundle

これで webinar の実演どおりの結果が得られる。

以下は小技。
  • ServiceMix のコンソールからlog/displayでログが見られるが、大して便利なものでもなく、普通に、別途コマンドプロンプトを開いて tail で log を流しておく方がマシ。ログファイルの場所は、FUSEフォルダ下の data/log/servicemix.log
  • ServiceMixに eclipse のリモートデバッガを引っ掛けるには、環境変数SET SERVICEMIX_DEBUGにTRUEを設定しておく。参考リンク

2009年10月18日日曜日

自作ワークフローエンジン(笑)

ワークフローエンジン - 構築すべきか、せざるべきか?」という記事を読んで、いちいち同意。

自前のワークフローエンジンを自作したがる人が多くて困るよねという話。

既製品に比べて、自前ワークフローエンジンは明らかに低品質・低機能の上に高コストなのに、なぜか横行している。「車輪の再発明」というアホでもわかる愚行が、ことワークフローに限っては、まかり通っている不思議についての考察。

記事を要約すると、3対の自作したがり派の主張と、対する筆者の反論。
  1. We only have very basic requirements(ワークフローエンジンを使う程難しい仕様じゃない)→ たいていの場合その後の仕様変更や機能拡張に対応できず、メンテ地獄に陥る。

  2. We need to integrate the engine into your application(他人の作ったシステムに依存して振り回されるのがイヤ)→ 軽量でカスタマイズ容易な組み込み可能製品もあり、理由にならない。

  3. We have evaluated, it does not fit(既存の製品を評価してみたが、合わなかった)→ 本当にその製品の理解に至る前に諦めて、思考停止して自作に逃げているだけ。

まあ、もっともだけど、ともすると自作したがり派が組織の中で主導権・決定権を持ってしまう問題が、実はもっと根本のところにある気がしないでもない。

いずれもう少しちゃんと考えてみたいけど、自作したがり派の人たちは概ね以下のような特徴がある
  • わりとスキルが高い:自作したがり派の人達は大抵はプログラムを書くのが好き。好きこそももの上手なれと言うくらいだから、好きな分だけ、なまじスキルが高い

  • 勉強が苦手:出回っている情報を効率よく集めて体系立てたり、他者の知的成果物を再利用する事が下手クソ。脳内の手持ち情報と、習慣による身体感覚だけで考え、またそこに快感を感じているらしい。知識の相場が分からないから、自分の無知に鈍感で「自分が何を知らないか」について無頓着。

  • 自己愛過多:「自分が作った」、「自分で考えた」に無駄にこだわる。過去の体験や、現に直面している問題が、かけがえのない自分だけに特有のものと考えたがり、「多くの先達が体験済みのありふれた経験」、「とっくに解法が知られている平凡な問題」だとは思いたがらない。で、オリジナリティに取り付かれた結果、「車輪の再発明」に陥る。

困った事に、こういった「自分」「自作」「自前」にこだわった自作したがり派が、組織環境によっては、「クリエイティブな人」という事で、意外と評価されて決定権をもってしまう事がある。こうなると、もう開発対象のワークフローエンジンに「車輪の再発明」が適用されてしまう事はまず避けられない。ああめんどくさ。

それはそうとして、記事の最後にあるように、DB/APサーバやORマッパー/DIコンテナを今どき自作なんてする人はいないのに、なぜワークフローエンジンだけは自作されるのかという疑問も残る。

多分、こんな感じか。
  • そもそもワークフローという知識のジャンルが知られていないから
    BPMN やワークフローパターンといった、既にかなり研究された知識体系があるのに、余り知られていない。で、ワークフローの知識に通じている人が人材市場にいても、プロジェクトオーナは身近にいる技術者をとりあえずアサインしてしまう。こうなるとワークフローに関する全問題が、製品仕様に固有の問題と捉えられ、自作したがり派の「自前で新たに作るしかない」という主張が真に受けられてしまう。

  • ワークフローは高レベルの知識領域だから
    AP/DBサーバやORマッパー/DIコンテナのような低レベルの製品は、やりたい事はほとんど自明で、後は使いやすさや性能の問題となるが、ワークフローエンジンのようなものは、上述した事情もあり、そもそも何をしてくれる製品なのかが自明とは言えない面がある。

  • ワークフローという言葉の意味が曖昧だから
    日本で「ワークフロー」というと、まず業務文書ありきで、文書のライフサイクル(状態遷移)に着目して、それを流れと捉えるもので、「ワーク」の「フロー」と言うより、「文書」の「フロー」と言ったニュアンスがある。本来のワークフローは、正に「ワーク」の「フロー」であって仕事の流れが主で、その中で複数のドキュメントが登場したり、或いはしなかったりする。こういった日本的な事情からか、単に抽象度のレイヤが違うだけの話が、「欧米で作られたワークフロー製品は日本のビジネス習慣に合わない」なんて主張されたりする。

まあ、とは言っても、僕も以前たずさわったプロジェクトではワークフローのフレームワークを自作した事があったりもするんだけど。

でもまあ、今後ワークフロー関連プロジェクトに携わって、尚且つ設計面で権限があれば、もう自前のワークフローエンジンなんて選択肢はありえない。これだけいろいろなワークフローエンジンが出揃った状況で、ワークフローエンジンの自作があるとしたら、きっと無知以外に要因は無いんだろうと思う。

2009年10月14日水曜日

JSF2.0 追試

JSF 2 を試したくて、developerworks に載ってる記事を追試してみた。

サンプルコードをdownload して、readme.txtを読んでやってみたが、動かすまでに一苦労あったので、動かすまでの手順などを書いておく。

■ javac でビルドしようとして失敗

サンプルコードをダウンロードすると Readme.txt があって動かす手順があるが、さっぱり動かない。動かす以前に、JavaファイルとGroovyファイルがあり、互いのクラスファイルを参照していたりして、そもそもビルドの仕方がよく分からない。

Groovyファイルのコンパイルってどうするんだっけ?
何やら ant タスクもあるらしいが、記事中でEclipse プラグインについて言及があったので、それを使うことにした。となるとGroovyのコンパイルだけ Eclipse というのも変だし、いっそ Eclipse ベースでやってみる事にした。

■ Eclipse ベースに移行
と言うわけで Dynamic Web Project を作成(すでにGroovyプラグインが入っている前提)。これだけではGroovyファイルがコンパイルできないので、プロジェクトのコンテキストメニューから Configure>Convert to Groovy Project を選択。Groovy nature が追加されてプロジェクトのアイコンがちょっと変わる。output フォルダはWEB-INFしたのclassesを指定しておく。あとビルドパスの設定で、Server Runtime>Apache Tomcat 6.0を追加する。


■ 必要ファイルをプロジェクトに追加

サンプルコードの必要ファイルを、Eclipse上のプロジェクトに以下のようにコピー。
  • places下のフォルダdecorators, pages, resources, sections, templatesとindex.htmlを
    WebContent 下にコピー
  • WEB-INF直下の3つのxmlを WEB-INF直下にコピー
  • src 以下のファイルをsrc下にコピー


続いて以下の jarをネットで漁るなどして集めて、WEB-INF/lib 下にコピー。
  • asm-all-2.2.jar
  • commons-beanutils-1.8.0.jar
  • commons-codec-1.3.jar
  • commons-fileupload-1.1.1.jar
  • commons-httpclient-3.1-alpha1.jar
  • commons-io-1.2.jar
  • groovy-1.6.5.jar
  • gwt-servlet.jar
  • hibernate3.jar
  • jsf-api.jar (mojarra-2.0.0-RC-binary)
  • jsf-impl.jar (同上)
  • jstl-1.1.0.jar
  • mysql-connector-java-3.1.13-bin.jar
(サンプルコードのreadmeで指定されているjarと若干違うが、Tomcat のバージョンでも違ったりするのか、そのままだと動かなかった(ClassNotFountException等)ので、試行錯誤して動く組合せをみつけた。)

■ 動かしてみる
http://localhost:8080/places/にアクセスするとユーザ名とパスワードを聞いてくる。なんのユーザ名かわからなかったが、ソースを見るとユーザ名:William, パスワードjsfで固定らしい。それを入力するとログイン成功。

「Go」ボタンを押すと地図が表示される。最初は例外が出るばかりで困ったが、スタックとレースを見ながら上述のようにjar の組合せを変えるとだんだん動くようになった。まだ何やら怪しい動きが多いが、ここまでくるとデバッガやログでいろいろ調べられる。徐々に改造してみようと思う。

■ とりあえずの雑感
  • Eclipse で Groovy と java が同じプロジェクトで同じようにコンパイルできて、ぜんぜん区別なく使えるのは初めて知った。Java プロジェクトに導入する敷居はかなり低いはず。特にEDSLに使えそう。上級プログラマが EDSL を開発して、若干スキルの低いプログラマが業務仕様をEDSL を用いて実装するような形で効果がありそう。
  • 最初の JSFはいまいち盛り上がらなかったけど、JSF2はどうなんだろうなあ。Struts 2とどっちがいいだろう。有用性とか生産性が同程度なら当然 JSF だけど、そのためにもやっぱり正式リリースをさっさと済ましてほしい。

2009年10月9日金曜日

Galileo/WTP/Tomcat/Glassfish/JBoss

久しぶりに Eclipse WTPをいじってみる。Galileoにしてから初めてかもしれない。APサーバもバージョンが上がっているから、この機会に全部最新にしてみる。

[ここ]を見ながら Snoop サーブレット作成とEclipse からのデプロイ・実行を試行してみた。以下、上手くいかなかったことその解決策をレポート。

環境
・Windows XP sp3
・eclipse Galileo (Eclipse IDE for Java Developers)英語版
・apache-tomcat-6.0.20
・jboss-5.1.0.GA
・glassfishv3
・glassfishv3-prelude

■1. WTP のインストール
[Help] > [Install New Software] でインストール
アドレスは→ http://download.eclipse.org/webtools/updates
4つのインストール項目が表示されるが、このうち
(1) Web Tools Platform (WTP) 3.1.1
(2) Project Provided Components
をチェックしておけば、とりあえず良し。(2)にはアダプタが含まれているので、チェックしておかないと後でサーバの追加ができない。


■2. プロジェクト作成とtomcat サーバの追加
WTP Tutorials を見ながら、昔ながらの Snoopサーブレットサンプルを作成。

まずは最初にServer Runtime Environment の追加。上で書いたとおりアダプタがない事に気付き追加インストールして、Tomcat の server runtime を新規作成に成功。

次に動的プロジェクトの作成。なぜか失敗。メッセージは"NullPointerException ”だけで意味不明。Project Facetsに Dynamic Web Module が選択されていない状態でプロジェクトが作成されてしまっている。作成したプロジェクトのコンテキストメニューから[Property]を開いて Dynamic Web Module にチェック。Eclipse に動的ウェブプロジェクトとして認識させて、手順どおりプロジェクトを完成させる。

ブラウザで動作確認→OK


■3. GlassFish サーバの追加
プロジェクトはそのままで、サーバだけ替えてGlassFish で動かしてみる。Tomcat と同様にServerRuntimeの追加から始めるが、必要なアイテムが見つからないといって失敗する。
どうやら、Eclipse に後から別途 WTP を追加した場合は、JST が入っていないのでプラグインの依存性が解決できずにインストールが失敗するらしい。で、JST を追加インストールして解決。→[参考]

上記問題の解決後、GlassFish v3 とGlassFish Preludeで試行し、どっちも成功。
ちなみに、GlassFish自体のインストールは、Server を新規作成するときに[Install Server]ボタンで Eclipse からインストールできる。


■4. Jboss サーバの追加
もともと入っていた Jboss 5.0beta を使いまわそうとしたが、アダプタと合わないらしく失敗。最新版の5.1GAをダウンロードして再試行してみたら上手くいった。アダプタは5.0用だったけど問題ないらしい。

サーバが立ち上がったので、ブラウザで確認しようとしたが 404 が出る。どうやらコンテキストパスが違うらしい。warのファイル名がそのまま使われているらしく、URLがhttp://localhost:8080/WTP-Tutorial-I/snoop になっている。そこでWEB-INF下にjboss-web.xmlを作成し、コンテキストパスを明示して再デプロイ。チュートリアルと同様に/tutorial/snoopで上手く実行できた。

下図は現在のサーバ群


これで、とりあえず最近のいろんなJava EE 実装の実験をする準備ができた。

2009年10月7日水曜日

Java 7のモジュール

Simple Module SystemはJSR294を救えるか?」を読んだ。最近は、ほとんど InfoQ の記事だけあれば、興味が満たされておなか一杯。

記事は JSR 294 が抜本的に見直されるかもしれないという内容。JSR 294 は、JSR 277 と共にJavaのモジュールの扱いを改善する仕様で、どちらも、もう何年も前からわりとよくアナウンスされてきた。

個人的な理解だと、JSR 277の方は、.NET の GAC のJava版かな。やっぱり今の Java のクラスパスや jar の扱いは、.NETに比べるとちょっと原始的。この仕様では、jar に替わる jam (.NETアセンブリみたいなもんか)なるものを用いたバージョンや依存性の管理、またリポジトリなんかの仕組みも定義されるとのこと。

デプロイ時のモジュラリティを扱う JSR 277に対して、JSR 294の方は superpackage というモジュール単位を導入して、主に言語仕様レベルで開発時のモジュラリティを改善するというものらしい。個人的には、要するに 「Public versus published」の考え方が導入されるのだろうと、ざっくり理解。

といった予備知識で、記事をさらさらと眺めていくと「メタモジュールシステム」なんて難しげなキーワードが出てくる。うーん、そんなのあったっけ?で、読み進むと、そのメタモジュールシステムを止めちゃえば?と言う話の流れらしく、ちょっと議論についていけなくなる。まあ細かい事は正式リリースしてからでいいや。

それにしても、Java 7開発は、もうちょっとスマートにプロジェクト管理できないもんかなあ。まあ、言語仕様にも関わるだけに、とりあえずリリースしてダメなら直すなんてのが許されないのは解るけど、もう何度も延期してないか? SI現場ならリスケはだいたい一回までが常識なんだが。Java 7開発自体、デスマーチ化してるんじゃないだろうか。

結局 Java SE 7って、正式リリースはいつなんだろう? 1年半ごとにJava5、6、7とリリースする予定だった気がするが・・・。Java EE 6から JBI が外されたりもしてるらしいし、SEも EEもグダグダ感がひどい。

JSR 277: JavaTM Module System
JSR 294: Improved Modularity Support in the JavaTM Programming Language

工業的手法からの類推について雑感

カンバン方式を適用する間違った理由と正しい理由」という記事。

「その新しい手法を導入するのは何故か?」という問いの答えにも良し悪しがあるという、当たり前な話だが、記事では、カンバン方式の導入について10個の正しい理由と5個の間違った理由が述べられている。

うーん、そもそも、日本発のカンバン方式の話題なのに、日本人の自分が良くわかっていなかったりする。カンバン方式を発展させたリーン方式の方は BPM の勉強で少しかじったけど、システム開発というより製造業の合理化手法としての側面だったので、開発プロセスの文脈で語られてもイマイチピンとこない。確か 自分の周りで Agile が流行りだした頃(2003年あたり)から、リーンはアジャイルプロセスに含まれていたと思うけど、何故だか今までちゃんと調べてこなかった。

個人的に、ソフトウェア開発に製造業や建築業からのアナロジーを持ち込むのに抵抗感がある。最近はそうでもなくなってきた気もするが、ちょっと前まではプログラマを最下層の単純労働者---たとえば、自動車工場におけるライン工、建設現場における穴掘り人足、野麦峠の製糸工のような---程度にしか捉えていない「ソフトウェア開発」観が、今よりもっと優勢だった。コボラー上がりの PM/PL がまわしているウォーターフォール型のプロジェクトには特に付き物で、そういった謬見のおかげで自分もチームの仲間もしょっちゅう酷い目にあった。だから今でも、他業種からの類推をソフトウェア開発に導入しようとする試みには、斜に構えてしまう。

まあ、でも本当に学ぶべきものがあるとしたら、見過ごすのはもったいない。そのうち調べておこうと思う。

2009年10月3日土曜日

日銀短観2009/09・・・まだ厳しそう

日銀短観が発表され業況判断DI(大企業・製造業)が2期連続上昇したとニュースで言っていたので、ググって見るとどうやら日銀のサイトで閲覧できるらしい。→[参照]
今まで見た事なかった。

なるほど、去年3月から今年9月まで3ヶ月ごとに、11, 5, -3, -24, -58, -48, -33 ポイントとなっており、確かに二期連続で上昇している。→[参照]
他の業種や企業規模もあるが、製造業の大企業がもっともよく景気を反映するという事で、ニュースでよく取り上げられるらしい。じゃあ情報サービス業はどうだろう?見ると以下のような数字になっている。

20082009
03060912030609
製造・大115-3-24-58-48-33
情報・全2823112-8-23-24
情報・大4232132-10-21-27
情報・中堅1920134-7-15-18
情報・中小23178-2-9-31-27

draw20091002(null)


(上の表の各行をクリックすると、表の下に折れ線グラフが表示される。前のブログエントリで試した canvas を用いて描画している。)

うーん、中小の最後以外、全部下がってるな…
まあ、この業界は他の業種で業況がよくなって設備投資が増えて、そして仕事が回ってくるという順番だろうから、もうしばらく厳しいか。

それにしても、こういうの XML で公開してくれないのかな。そうしたら XSLT で見たいところだけ取り出してJavaScriptなんかで加工するような事が楽なのに。

今のところ、「要旨」がHTML(しかも表の部分は PRE内のプレーンテキスト)で、「概要」、「業種別計数」、「調査全容」が PDF と Excelで提供されている。プログラムで加工できない事もないだろうが、明らかにXMLより面倒くさい。

多分、日銀のサイト作ってる人の中にも、XML にしたいなって思ってる人いるんじゃないだろうか。プログラマなら誰でもそう考えそうな気がする。他の官公庁でも、いろんなレポートやらを公開するとき XML にすればいいのになあ。

2009年10月1日木曜日

bloggerでcanvasを使ってみる(IEにも対応)

JavaScript で、図表や記号、例えば簡単な譜面の断片のようなものを描けないかと思って調べていると、canvas タグと言うものでかなりの図形描画ができるらしい。ただし、やはりというか、またしてもというか、ここでもIE が問題になる。

まずcanvas タグがそもそもサポートされていない。これは ExplorerCanvas というスクリプトがあって、これを読み込んでおけば canvas への操作を VML でエミュレートして同等の効果が得られるらしい。パフォーマンスが悪くなるという指摘もあるみたいだが、こここまでは、まあ、とりあえず良し。

で、blogger でも使ってみようと思うのだが、ブログの性質上 JavaScript は <body>内に記述されることになる。ところが、またまた IE で問題が生じる。普通に body 内の script タグ中に記述した canvas 操作コードが思ったとおりに実行されない。ExplorerCanvas がちゃんと効いていないのか、canvas のgetContent()を呼んだところでエラーになる。どうも onload 以降のタイミングでないとちゃんと動かないらしい。

そこで blogger の「HTML/JavaScript の設定」で以下のようなコードを追加して、canvas タグの直後の要素を描画コード記述部とみなして、その innerHTML が onload のタイミングでJavaScriptとして実行されるようにしてみた。
function drawAll() {
  canvases = document.getElementsByTagName('canvas');
  for (var i = 0; i < canvases.length; i++) {
    var canvas = canvases[i];
    var className = canvas.getAttribute('class')||canvas.getAttribute('className');
    if ('blogger' == className) {
      var script = canvas.nextSibling;
      if (script.nodeName=='/CANVAS') script = script.nextSibling
      eval(script.innerHTML.replace(/&gt;/g, ">").replace(/&lt;/g, "<"));
    }
  }
}
if (window.addEventListener) {
・・・他のイベントリスナ
  window.addEventListener('load', drawAll, false);
} else if (window.attachEvent) {
・・・他のイベントハンドラ
  window.attachEvent('onload', drawAll);
} else {
・・・他のイベントハンドラ
  window.onload = drawAll;
}
個々のブログエントリには、例えば以下のように canvas と描画コードを記述する。(本来は改行を削除して、"<"と">"をエスケープする必要があるが、わかりにくくなるため省略した。)
<canvas id="c1" width="150" height="150" class="blogger" style="border:1px solid black;background-color:#FFFFCC;"></canvas>
<pre style="display:none;">
  var canvas = document.getElementById('c1');
  var ctx = canvas.getContext('2d');
  for (i=0;i<6;i++){
    for (j=0;j<6;j++){
      ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' +
                        Math.floor(255-42.5*j) + ')';
      ctx.beginPath();
      ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);
      ctx.stroke();
    }
  }</pre>
JavaScriptコードは canvas tutorial から取ったサンプルだが、参照先と同じ図形群が以下のように表示されていることを、FireFox、Safari、Opera、IE の最新版で確認した。(確認していないブラウザだと表示されていないかもしれない。)
var canvas = document.getElementById('c1');var ctx = canvas.getContext('2d');  for (i=0;i<6;i++){    for (j=0;j<6;j++){      ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' +                        Math.floor(255-42.5*j) + ')';      ctx.beginPath();      ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);      ctx.stroke();    }  }

それにしても、IEはなにかと面倒くさいなあ。DHTML や JavaScript で何かやろうとするたびに、IEでイライラする。すでに使い勝手やパフォーマンスの面では、(ちょっと言葉は悪いが)「糞ブラウザ」という評価もあるようだけど、Web アプリ開発者にとっても何か嫌な感じだ。IE のシェアがもっと落ち込んで、無視しても問題ないくらいになれば良いのに、こんなのが OS と抱き合わせだからタチが悪い・・・