Javaの最近のブログ記事

CheckStyleでJavaのソースコードをチェックしているとき、以下のような警告が表示されることがあります。

最初の一文はピリオドで終わらなければなりません。


ピリオドだけでなく句点「。」でコメントが終わっている場合は警告を表示させなくするために、以下のようにCheckStyleのルールを修正します。

「CheckStyle構成」->「Javaコメント」->「スタイルJavadoc」を選択した状態で「開く」ボタンをクリック。
表示されるダイアログの「endOfSentenceFormat」を次のようにします。

([。.?!][ \t\n\r\f<])|([。.?!]$)

※句点を2ヶ所追加します。

正しく動作するかを確かめるために「テキスト文字列をここに入力」と書かれているテキストボックスにいくつか文字列を入れてみましょう。句点が最後に入っている場合は、背景色がグリーンになります。

この修正を行ったことをエラーメッセージにも反映させるには、「拡張」タブの「javadoc.noperiod」のメッセージを「最初の一文はピリオド、または句点で終わらなければなりません。」(例)と修正します。
以下のような前提の時、Jarファイルをeclipseから実行させる場合に注意が必要です。

【前提】
・Jarファイルから外部ファイルを読み込み、もしくは書き込みする。
・外部ファイルのパスを相対パス指定している。

eclipseで表示されているJarファイルを実行すると、eclipseの置かれているフォルダが作業フォルダ(カレントディレクトリ)になります。例えば、「C:\Program Files\eclipse」というようなパスです。

Jarファイルで外部ファイルを相対パス指定していると、作業フォルダからのパスを取得することになります。他の環境に配布する前提なのであれば、エクスプローラ上でJarファイルを直接起動するようにしましょう。
Javaで実装されたテンプレートライブラリApache Velocityは、メールのテンプレート生成等によく利用されています。そのVelocityライブラリを利用している際によく発生する ResourceNotFoundException(org.apache.velocity.exception.ResourceNotFoundException) の対処法についてまとめてみます。


【基本編】 どういった状況で ResourceNotFoundException が発生するか

VelocityのJavadocはとても不親切なので分かりづらいのですが、テンプレートファイルが指定した位置に存在しないことが大きな原因の1つです。
Templateクラスのインスタンスを生成するVelocityクラスもしくはVelocityEngineクラスのgetTemplateメソッドを呼ぶ際に、引数として与えたテンプレートファイルへのパスが誤っていることをまず疑ってください。

- ファイル名は誤っていないか
- テンプレートファイルのあるディレクトリ名が誤っていないか


【発展編】 パスは誤っていないはずなのに ResourceNotFoundException が発生する場合

何度確認してもパスに間違いがない場合は、getTemplateメソッドの引数について次の点を確認しましょう。(FileResourceLoaderを利用している場合のみ)
  ・ 相対パスで指定しているか
  ・ XXX.resource.loader.path(XXXは、リソースローダ名) プロパティからの相対パスになっているか
  ・ XXX.resource.loader.path配下にあるファイルを指定しているか(サブディレクトリ内でも可)

3つ目が案外はまりやすいんじゃないでしょうか。
デフォルトでのresource.loader.pathはカレントディレクトリになっています。つまり、カレントディレクトリ配下にテンプレートファイルが配置されていないといけません。テンプレートファイルをカレントディレクトリ以外に配置したい場合は、resource.loader.pathを適切に設定する必要があります。

また、Windows環境で実行している場合は次の点も確認してみましょう。
  ・ XXX.resource.loader.pathを指定するとき、「\」でなくて「/」を利用しているか


【参考資料】
[wiki] Velocity - 気の向くままに
[web] Velocity - TECHSCOPE
[web] Apache Velocity (本家)
行列式を手で簡単に解く方法として、サラスの方法があります。
もちろんプログラムでもサラスの方法は利用することはできるのですが、この方法では4次以上の行列に対して行列式を求めることができません。そこで、4次以上の行列の行列式を求めるために、余因子展開を用いたJavaプログラムのサンプルを紹介します。

(なお、確実にテストを行ったわけではないので、自己責任の範囲内で利用してください。バグの報告はコメントにお願いします。)

public class Sample {
	private static interface ArrayAccess {
		
		int size();
		double get(int r, int c);
	}
	
	private static class WrapEntity implements ArrayAccess {

		private ArrayAccess array;
		private int row;
		private int col;
		private int size;
		
		public WrapEntity(ArrayAccess a, int r, int c){
			this.array = a;
			this.row = r;
			this.col = c;
			this.size = a.size()-1;
		}
		
		public double get(int r, int c) {
			r = r>=row ? r+1: r;
			c = c>=col ? c+1: c;
			return array.get(r, c);
		}

		public int size() {
			return size;
		}
		
	}
	
	private static class Entity implements ArrayAccess {
		
		private double[][] array;
		private int row;		
		private int col;
		private int size;
		
		public Entity(double[][] a, int r, int c){
			this.array = a;
			this.row = r;
			this.col = c;
			this.size = a.length-1;
		}
		
		public int size(){
			return size;
		}
		
		public double get(int r, int c){
			r = r>=row ? r+1: r;
			c = c>=col ? c+1: c;
			return array[r][c];
		}
	}
	
	private static double sub(ArrayAccess array){
		if(array.size()==1){
			return array.get(0, 0);
		}
		if(array.size()==2){
			return array.get(0, 0)*array.get(1, 1)
					-array.get(0, 1)*array.get(1, 0);
		}
		// 行列式の計算
		double det = 0;
		final int length = array.size();
		for(int i=0;i<length;i++){
			double v = array.get(0, i);
			if(v!=0){
				double d = v*sub(new WrapEntity(array,0,i));
				det += i%2==0? d: -d;
			}
		}
		return det;
	}
	
	public static double cofactor(double[][] array){
		// 引数チェック
		if(array==null){
			throw new NullPointerException();
		}
		final int length = array[0].length;
		for(int i=0; i<array.length; i++){
			if(array[i].length!=length)
				throw new IllegalArgumentException();
		}

		// 行列式の計算
		double det = 0;
		for(int i=0;i<length;i++){
			double v = array[0][i];
			if(v!=0){
				double d = v*sub(new Entity(array,0,i));
				det += i%2==0? d: -d;
			}
		}
		return det;
	}

// サンプル用メインメソッド
public static void main(String[] args){ double[][] array = {{1,2,3},{4,5,6},{7,8,9}}; System.out.println(cofactor(array)); } }
ここで、簡単にソースの説明を。
前半のArrayAccessインターフェース、WrapEntityクラス、Entityクラスは、配列の配列を余因子行列のように見せるためのエンティティクラスです。このクラスを利用しない場合は、配列の内容を毎回コピーする必要があるので、処理速度を高めるために導入しています。

後半のsubメソッド、cofactorメソッドが行列式を計算するメソッドになります。
2つのメソッドはほぼ同じ処理をしていますが、cofactorでは引数チェックを行っている点が異なります。また、subメソッドでは余因子展開を再帰を用いて計算しています。

もう少し考えればもっと効率は良くなるかと思いますが、とりあえずは処理の流れが理解しやすい今の状態を公開することにします。
ApacheプロジェクトのうちのApache Commons Mathライブラリでは、行列計算を簡単に行うためのAPIが提供されています。もし、Javaで行列計算をさっさとやってしまいたい場合などは非常に役に立つと思います。

Apache Commons Mathは次のページからダウンロードできます。
[web] Aapche Commons Math
このページの「Releases」と書かれたタイトルの下にあるリンクから、ダウンロードページに行けます。現在の最新リリースは、Ver.1.2 です。

Commons Mathのライブラリを取得してきたら、javadocを見ながら早速作成してみましょう。
このライブラリの中で、行列計算をするために重要なのは、MatrixUtilsクラス、BigMatrixインターフェース、RealMatrixインターフェースです。
(パッケージは全てorg.apache.commons.math.linear です。)

行列計算をさせる流れは以下の通り。
1.行列として扱いたいデータ(配列の配列)を用意する。
2.MatrixUtilsクラスのcreateXXXMatrixメソッド(XXXは、RealやBig等と置き換わります。詳細はAPIを参照。)を使って、BigMatixもしくはRealMatrixのインスタンスを生成。
3.生成したインスタンスを利用して、行列計算

ちなみに、このRealMatrixインターフェース等を見てもらうと分かる通り、生成されるインスタンスは不変ではありません。複数のスレッド間で参照・変更をする場合は、必ず同期をとる必要があります。

public static void main(String[] args){
  double[][] array = {
    {1,2,3},
    {4,5,6},
    {7,8,9}
  };
  RealMatrix matrix = MatrixUtils.createRealMatrix(array);
  System.out.println(matrix.getDeterminant());  # 行列式の表示
}
Velocityには独自に処理内容をログに出力する機能が含まれています。
特に気にしない場合はいいのですが、実際に運用する場面では冗長な機能ともいえます。そんな時にどのようにしてログを出力させないようにするかを紹介します。

まず、単純な方法は、Velocityにプロパティとして与えることです。

Velocity.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS, 
    "org.apache.velocity.runtime.log.NullLogSystem");
これを初期化処理と一緒にやってしまうといいでしょう。
その他にも、プロパティファイルに定義したい場合は以下のようにします。

runtime.log.logsystem.class=org.apache.velocity.runtime.log.NullLogSystem
この一行をプロパティファイルに追記してください。

【参考文献】
[blog] Velocityのログ出力をオフにする - Oddwit
[web] Velocityの設定キー - TECHSCORE
ある定型のメールの文章のうち、一部分だけを変えて送信したいということは往々にしてあり得ます。しかし、その処理を実装しようと思うと案外面倒だったりします。そこで、その処理を簡単にするために、Apache Velocityを用いたメールテンプレートの利用方法について紹介します。

【前提条件】
- J2SE 1.2以上
- Apache Velocity1.5
- Apache Commons Collection3.1, Apache Commons Lang2.1
- テンプレートファイル(.vm)
- プロパティファイル(.properties):必須ではない。以下の例では利用

【使い方】
Apache VelocityはJ2SE以降で動作する汎用テンプレートエンジンです。これを利用することで、メールテンプレートはもちろん、XMLやSQLなど、あらゆるテンプレートに利用可能です。使い方はアイデア次第といったところです。

で、実際に利用するためにApacheプロジェクトのサイトからjarファイルをダウンロードしてきます。
[web] Apache Velocity

このページの左側に「Downloads」と書かれたリンクがあるので、その先のページからzipファイルかtar.gzファイルをダウンロードします。

そして、ダウンロードしてきたファイルを解凍するとVelocityのjarファイルが手に入ります。ただ単に試したいだけならvelocity-dep-X.jarを利用すればいいでしょう。velocity-X.jarは、Velocityが利用している外部ライブラリを含んでいないjarファイルです。

velocity-dep-X.jarをクラスパスで指定し、以下のプログラムを実行することで動作を確かめられます。Velocityで提供されている主要なクラスは、VelocityContextクラスとVelocityEngineクラスです。

    public static void main(String[] args) throws Exception{
        // 購入者情報
        Member member = new Member();
        member.setLastName( "佐藤" );
        member.setFirstName( "太郎" );
        member.setEmail("aaa@example.com");
        
        // 購入商品情報
        Item item1 = new Item();
        item1.setItemCode("0000-1111" );
        item1.setName( "健康食品1" );
        item1.setPrice(1000);

        Item item2 = new Item();
        item2.setItemCode("0000-1112" );
        item2.setName( "健康食品2" );
        item2.setPrice(2000);
        
        // 注文情報
        Order order = new Order();
        order.setMember( member );
        order.setOrderNumber("0001-AAAA");
        
        order.addItem(item1);
        order.addItem(item2);
        
        // 会社情報
        Company company = new Company();
        company.setName("なんでも.com");
        company.setEmail("bbb@example.com");        
        
        VelocityWrapper va = new VelocityWrapper( "mail.vm" );
        va.put( "order" , order );
        va.put( "company" , company );
        String body = va.merge();
        
        System.out.println(body);  
    }

    public class Company {

	private String name;
	private String email;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
    }

    public class Item {

	private String itemCode;
	private String name;
	private int price;
	
	public String getItemCode() {
		return itemCode;
	}
	public void setItemCode(String itemCode) {
		this.itemCode = itemCode;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int i) {
		this.price = i;
	}
    }

    public class Member {

	private String lastName;
	private String firstName;
	private String email;

	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}	
    }

    public class Order {

	private Member member;
	private String orderNumber;
	private List itemList = new ArrayList();
	
	public Member getMember() {
		return member;
	}
	public void setMember(Member member) {
		this.member = member;
	}
	public String getOrderNumber() {
		return orderNumber;
	}
	public void setOrderNumber(String orderNumber) {
		this.orderNumber = orderNumber;
	}
	public List getItemList() {
		return itemList;
	}
	public void setItemList(List itemList) {
		this.itemList = itemList;
	}
	public void addItem(Item item1) {
		itemList.add(item1);		
	}
    }

    public class VelocityWrapper {

	/** テンプレートファイルの内容を格納したクラス */
	private Template template = null;

	/**
	 * テンプレート変換時に使用する オブジェクトを格納するためのクラス
	 */
	private VelocityContext context = new VelocityContext();

	/** Velocityエンジンにアクセスするためのクラス */
	private VelocityEngine engine = new VelocityEngine();

	public VelocityWrapper(String templateFileName) throws IOException,
			Exception {

		// velocity.propertiesによるVelocityEngineの初期化
		Properties props = new Properties();
		props.load(new FileInputStream("velocity.properties"));
		engine.init(props);

		// テンプレートの取得
		template = engine.getTemplate(templateFileName);
	}

	public void put(String key, Object value) {
		context.put(key, value);
	}

	public String merge() throws ResourceNotFoundException,
			ParseErrorException, MethodInvocationException, Exception {
		StringWriter sw = new StringWriter();
		template.merge(context, sw);
		return sw.toString();
	}
    }
以下がvelocity.propertiesです。
input.encoding=Windows-31J
output.encoding=Windows-31J
以下がテンプレートファイル(mail.vm)です。
$order.member.lastName $order.member.firstName 様

$company.nameをご利用頂きありがとうございます。

以下の注文を承りました。
注文番号:$order.orderNumber

≪購入商品情報≫
-----------------------------
商品番号 |注文商品名|価格
-----------------------------
#set ($amount=0)
#foreach ( $item in $order.itemList)
$item.itemCode|$item.name|  $item.price
#set( $amount=$amount+$item.price)
#end
-----------------------------
         合計          $amount
#set ($point=0)
#if($amount<1000)
#set ($point=10)
#elseif($amount<2000)
#set ($point=20)
#else
#set ($point=30)
#end

獲得ポイントは$pointです。

=================================================
$company.name
メールアドレス:$company.email
=================================================
なお、この記事を書くために、CodeZine様のページをとても参考にさせていただいています。(ただし、本文中の誤記が非常に多いです。)
[web] Jakarta Velicityでテンプレートを変換しメールを送信する - CodeZine
9月26日の日経新聞に、XBRLが経済界で注目されているという話を知りました。
XBRL(Extensible Business Reporting Language)とは、財務情報が作成・流通・再利用できるように標準化されたXMLベースの言語です(東京証券取引所HPより。)。これを利用することで、これまでは統一して扱うことができなかった財務情報を簡単に取り扱うことができるようになります。現在では、東証一部に上場している多くの企業がXBRLで財務情報を公開しています。

[web] XBRLとは - 東京証券取引所
[web] EDINET (XBRLがダウンロード可能)

さて、このXBRLを扱うためのツールも多く存在しますが、Javaでデータを扱いたいという場合もあるでしょう。そのような要求のために、XBRL APIがsourceforgeで提供されています。やや慣れが必要ですが、これを用いることで通常のXMLファイルのように取り扱えるようになれます。

[web] XBRLAPI.ORG HOMEPAGE (英語)

このAPIを利用するためには数多くのサードパーティAPIが必要になります。以下に、最低限必要なAPIを列挙します。
・XBRL API
・XBRL Utilities
・XML Base
・XLink
・XML Pointer

この他にも、必要に応じて以下のAPIを用いることもあるようです。
・Log4j
・XML-API
・Xerces
・JUnit
・Xalan
・eXist
・Xindice
・XML:DB
・XMl RPC
・Apache Commons Logging
・db (Berkeley DB Java interface)
・xbxml (Berkeley DB XML overlay)

これらのAPIは以下のサイトからダウンロードできます。ダウンロードしてきたら、Javaのクラスパスの通っているフォルダに配置します。
[web] Java XBRL API Implementation - sourceforge.net
[web] XBRL SVN Repository

そして、Log4jの出力をコンソールにするために、Log4j用のXMLファイルをダウンロードします。
[web] /trunk/conf/log4j.xml - XBRL API SVN Repository

次に、XBRLファイルを処理するためのキャッシュ・ディレクトリを用意します。これは書き込み権限があるディレクトリならどこでもいいので、カレントディレクトリでも、適当なディレクトリにしてもいいでしょう。

さて、ここまで来たら実際に使えるか試してみましょう。
java -jar xbrlapi-api-YYYYMMDD.jar -logger [LOG4J.CONFIG]
-class org.xbrlapi.data.dom.Load -cache [CACHE.LOCATION] [URL]+
[LOG4J.CONFIG] はLog4j用のコンフィグファイルへのパスを指定します。また、同様にして[CACHE.LOCATION]は、キャッシュディレクトリへのパスを指定します。

[URL]が、解析をするXBRLファイルへのURLです。
これがやや曲者で、ローカルファイルを指定するときは、「file:///」を先頭につけて絶対パスで記述する必要があります。Unix系OSなら書きやすいですが、Windowsの場合は「file:///c:/document/...(略).../xbrl-instance.xml」などと記述する必要があります。

この時点で例外が発生する場合は次の点に注意してみましょう。
1.クラスパスにライブラリがすべて含まれているか。
2.ライブラリの指定の仕方が間違っていないか。(Unix系はコロンで、Windowsはセミコロンでパスを区切ります。)
3.XBRLファイルへのURLが間違っていないか。


ここまで来れば、XBRL APIに含まれるJUnit用のテストケースを参照することで、なんとなく使えるようになるのではないでしょうか。

もしAPI中のJUnitのテストを行うためには、プロパティファイルが必要になります。下記からダウンロードしてください。
[web] /trunk/test.configuration.properties -XBRL API SVN Repository
[web] testing configuration file - XBRL.ORG (プロパティファイルのマニュアル)

プロパティファイルの「local.cache」というところだけは最低限修正して、下記のコマンドを実行します。
java -Xmx1g -Dxbrlapi.test.configuration=[PATH.TO.CONFIGURATION.FILE]
junit.textui.TestRunner org.xbrlapi.tests.AllTests
[PATH.TO.CONFIGURATION.FILE]は、プロパティファイルへのパスです。


最後に、Eclipseで開発を進めたい方に。
Eclipseの場合はSubversionから一度にチェックアウトすることが可能です。手動でjarファイルをダウンロードするのが面倒くさい人はこちらの方法を試してみてください。リポジトリのパスは、https://xbrlapi.svn.sourceforge.net/svnroot/xbrlapi です。

XBRL APIのDOMLoadingTestCaseクラスやAllTestsクラスのソースは、開発の役に立つと思います。また時間が取れれば、サンプルプログラムも載せてみたいと思っています。
カブドットコム証券でFX(外国為替保証金取引)を行うための画面が表示されなくなる場合があります。これは、Javaのバージョンを1.6 update 5に変更したときに生じる不具合ということらしいです。(Javaをアップデートしていなければ別の原因が考えられます。)

これに対処するためには、Javaのコンソール(メッセージを表示する画面)を表示させるように設定を変更すればいいようです。変更の仕方は、「コントロールパネル」→「Java」→「詳細」タブ→「Javaコンソール」→「コンソールを表示する」にチェックすればOKです。FXの取引や、その他のJavaを利用するプログラムでコンソールが出るようになりますが、これは閉じても大丈夫ということです。

早期に対策が望まれるところですね。
JavaのLogging APIは、通常の開発の場面ではもちろん、常時動作するサーバなどの用途でも用いられるログ出力用のAPIです。最近このAPIに興味を持ったので、ちょっと調べてみました。

[web] Java Loggin API
[web] JavaでHello World ログ機能編

で、具体的な使い方については上のURLを参照していただくとして、ここでは1回のログ出力を1行にまとめるHanlderを掲載します。デフォルトのログフォーマットは複数行にわたって出力されることが多いのですが、これだとシェルでgrep検索をするときにちょっと面倒です。

そこで、Java Logging APIというページでも紹介されているFormatterを少し改善して、Java6でも使えるものを紹介します。(Java Logging APIで紹介されているMyFormatterクラスでは、Java 1.4beta2ですでにJava APIから削除されたメソッドが使用されています。)


import java.util.logging.Formatter;

final class MyFormatter extends Formatter {

private Calendar calendar = Calendar.getInstance();

public synchronized String format(LogRecord record) {
StringBuilder message = new StringBuilder(100);
calendar.setTimeInMillis(record.getMillis());

message.append(
String.format(
"%1$tD %1$tT %2$s ",
calendar,
record.getLevel().toString()));

if (null != record.getSourceClassName()) {
message.append(
record.getSourceClassName());
} else {
message.append(
record.getLoggerName());
}
message.append(' ');

if (null != record.getSourceMethodName()) {
message.append(
String.format(
"[%s]",
record.getSourceMethodName()));
}
message.append(' ');

message.append(formatMessage(record));
message.append('\n');

if (null != record.getThrown()) {
message.append(
record.getThrown());
}
return message.toString();
}
}
あとは、これを出力先のHandlerに与えればOKです。

Advertizement

このアーカイブについて

このページには、過去に書かれたブログ記事のうちJavaカテゴリに属しているものが含まれています。

前のカテゴリはDebianです。

次のカテゴリはOpenOfficeです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Powered by Movable Type 5.0