プログラマメモ2 - programmer no memo2

[java]URIとURL 2006/08/31
2008/01/20

java.net.URIクラスには toURLメソッドが用意されています。

ですが、java.net.URLクラスには、toURIメソッドがありません。



何故にそういう疑問をもつにいたったかといいますと、





URI は Uniform Resource Identifier であり、URL は Uniform Resource Locator です。したがって、抽象的に言えば、すべての URL は URI ですが、すべての URI が URL であるとはかぎりません。



javadoc1.4 日本語版 java.net.URIの説明より





という説明を読んで、単純にすべてのURLはURIなら、toURIメソッドをURLクラスがもっていてもいいのではないか、と思ったからです。



似たような疑問をもった方がいました。

http://www.teria.com/~koseki/emacswiki/FileUriUrl.html





相互変換は簡単にできます。

http://javaalmanac.com/egs/java.net/Url2Uri.html?l=rel



下記のコードは、http://javaalmanac.com/egs/java.net/Url2Uri.html?l=relで紹介されていたものです。



try {

url = uri.toURL();

} catch (IllegalArgumentException e) {

// URI was not absolute

} catch (MalformedURLException e) {

}



// Convert a URL to a URI

try {

uri = new URI(url.toString());

} catch (URISyntaxException e) {

}

[java][windows]tasktrayを出すもうひとつの方法 2006/08/30
2006/11/26

javaを使用してwindows上のタスクトレイを表示する方法です。



https://jdic.dev.java.net/
で開発されているJDIC(JDesktop Integration Components)を利用すると簡単にできました。



ライセンスはLGPLです。



swinglabsファミリーのコンポーネントです。







ネイティブなライブラリを使用しますので、

OSごとにパッケージが用意されています。

MacOSXはまだないようですね。



ダウンロードから必要なパッケージをダウンロードします。

windowsで試すので、jdic-0.9.1-bin-windows.zip



展開されたファイルから、

tray.dll

jdic.jar

をとりだして、クラスパス、ライブラリパスを設定して使用します。

タスクトレイに表示させるためのクラスは、org.jdesktop.jdic.tray.TrayIcon.classです。



ネイティブアプリでしかできないと思っていたものが、javaでもできるとうれしくなりますね。

[java]privateフィールドにアクセスする方法 2006/08/27
2006/11/26

作成されたクラスのプライベートなフィールド変数にアクセスするための方法についてです。

java.lang.reflectパッケージのFiledクラスを使用します。
Fieldクラスは、AccessibleObjectクラスのサブクラスです。AccessibleObjectクラスには、public void setAccessible(boolean flag)throws SecurityExceptionというメソッドがあります。

リフレクトされたオブジェクトのjava言語アクセスチェックを行うかどうか設定できます。

任意のオブジェクトからリフレクトされたフィールドを取得する際に使用する命令は、java.lang.Classのpublic Field getField(String name)throws NoSuchFieldException,SecurityExceptionメソッドではなく
public Field[] getDeclaredFields()throws SecurityExceptionを使用します。

getFiledメソッドは、publicなフィールドを返しますので、使用する時点でフィールドにはアクセスできません。

privateフィールドにアクセスするサンプルコードです。

import java.lang.reflect.Field;

public class TestA {

/**
*
* @param args
* @throws SecurityException
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static void main(String[] args) throws SecurityException,
NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {

Aaa aaa = new Aaa();
// (1)アクセスできます
Field field = aaa.getClass().getDeclaredField("str");
field.setAccessible(true);
System.out.println(field.get(aaa));

// (2)アクセスできません
Field field2 = aaa.getClass().getField("str");
field2.setAccessible(true);
System.out.println(field2.get(aaa));

}

}

/*
* private なフィールドをもつクラス
*/
class Aaa {
private String str = "iamPrivateField";
}




Accessing nonpublic members
「Java Reflection in Action (In Action)」P.38



javaでクロージャがサポートされる? 2006/08/23
2006/11/26

[java]郵便番号でソートされた、データファイルを作成する 2006/08/10
2006/11/26

シナリオ:

郵便局のホームページからCSVフォーマットで作成されたデータファイルを加工して別のデータファイルを作成する必要があります。



郵便局のホームページから住所の郵便番号のダウンロードします。

ダウンロードするファイルは、全国一括です。

CSVフォーマットなのですが、データ量が多くてマイクロソフトのエクセルで処理できませんでした。

で、簡単なプログラムを書いて、読み込ませて、出力します。



import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;

public class TestYubinbangouDatafileCreator {

public static final char DEFAULT_SEP = ',';

public static List parse(String line) {
List list = new ArrayList();
StringBuffer sb = new StringBuffer();
int i = 0;

if (line.length() == 0) {
list.add(line);
return list;
}

do {
sb.setLength(0);
if (i < line.length() && line.charAt(i) == '"') {
i = quoted(line, sb, ++i);
} else {
i = plain(line, sb, i);
}
list.add(sb.toString());
i++;
} while (i < line.length());

return list;
}

protected static int quoted(String s, StringBuffer sb, int i) {
int j;
int len = s.length();
for (j = i; j < len; j++) {
if (s.charAt(j) == '"' && j + 1 < len) {
if (s.charAt(j + 1) == '"') {
j++;
} else if (s.charAt(j + 1) == DEFAULT_SEP) {
j++;
break;
}
} else if (s.charAt(j) == '"' && j + 1 == len) {
break;
}
sb.append(s.charAt(j));
}
return j;
}

protected static int plain(String s, StringBuffer sb, int i) {
int j = s.indexOf(DEFAULT_SEP, i);
if (j == -1) {
sb.append(s.substring(i));
return s.length();
}
sb.append(s.substring(i, j));
return j;

}

public static void main(String[] args) throws IOException {

TreeMap treeMap = new TreeMap();
String file = "/tmp/KEN_ALL.CSV";

BufferedReader reader = new BufferedReader(new FileReader(file));
String line = null;
StringBuffer buffer = new StringBuffer();
while ((line = reader.readLine()) != null) {
List list = parse(line);
String key = (String) list.get(2);
List list2 = new ArrayList();
list2.add(((String) list.get(0)).substring(0, 2));
list2.add(list.get(2));
list2.add(list.get(6));
treeMap.put(key, list2);
}

Iterator iterator = treeMap.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next().toString();

List list = (List) treeMap.get(key);
buffer.append(((String) list.get(0)).substring(0, 2));
buffer.append(list.get(1));

System.out.println(list);
}

saveFile(new File("/tmp/datafile2.txt"), new String(buffer).getBytes());
}

static public void saveFile(File file, byte[] b) throws IOException {

OutputStream out = null;
try {
out = new FileOutputStream(file);
out.write(b);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {

}
}
}
}
}


CSVを処理する部分は、適当にkodersでみつけたものを実装参考にしています。

もっと優雅にデータを加工したいものですね。

[java]郵便番号から都道府県コードをさがしだす。 2006/08/10
2006/11/26

住所の郵便番号ダウンロードサービスは郵政公社のWebサイトから行えます。

このデータがあれば、住所や、その他情報と郵便番号を結びつけることができそうです。





シナリオ:



都道府県コードが必要です。

郵便番号しかわかりません。

郵便番号と都道府県コードを結びつけたデータファイルは作成して、用意してあります。

そのファイルから郵便番号を使用して都道府県コードを検索します。







調べれば、簡単な式で郵便番号から都道府県コードをみつけることができそうですが、一応ファイルから検索するということにします。



ちなみにここでいう都道府県コード(JIS X0401)は2桁の数字です。



郵政公社のWebページからCSVデータをダウンロードして、加工して、データファイルを作成します。



CSVデータは都道府県コードで並んでいるので、データファイルを作成するときに郵便番号でソートしておきます。



できるだけデータファイルを小さくするため、区切り文字を使わないようにします。



9byteをひとつのデータブロックとして扱います。

データブロック=都道府県コード(2桁)+郵便番号(7桁)



作成したデータファイルのサイズは、約1メガ(1094661バイト)になりました。



このデータファイルを検索します。

検索方法はバイナリサーチで行います。





import java.io.File;

import java.io.IOException;

import java.io.RandomAccessFile;



public class TestSearchTodohukenCode2 {



public static void main(String[] args) throws IOException {

String filename = "/tmp/datafile.txt";

long t = System.currentTimeMillis();



File datafile = new File(filename);

String result = search(datafile, "9071801");

System.out.println(result != null ? "bingo:" + result : "failed..");

System.out.println(" time:" + (System.currentTimeMillis() - t));



}



public static String search(File datafile, String target)

throws IOException {



RandomAccessFile raf = null;

String ret = null;

class BinraySearch {



/*

* 検索できない場合はnullを返します。

*/

public String search(RandomAccessFile raf, long left, long right,

int intTarget) throws IOException {



if (right <= (left + 9))

return null;



byte[] bs = new byte[9];



long middle = (left + right) / 2;

// 9byteにあわせる

long remainder = middle % 9;

long pos = middle - remainder;



raf.seek(pos);

raf.read(bs);



byte[] bs2 = { bs[2], bs[3], bs[4], bs[5], bs[6], bs[7], bs[8] };



int get = Integer.parseInt(new String(bs2));



System.out.println("left:" + left + " right:" + right

+ " result:" + get + "taget:" + intTarget);

// bingo

if (get == intTarget) {

byte[] bs3 = { bs[0], bs[1] };

return new String(bs3);

}



if (get < intTarget) {

return search(raf, pos, right, intTarget);

}



if (intTarget < get) {

return search(raf, left, pos, intTarget);

}



return null;

}



}



try {

raf = new RandomAccessFile(datafile, "r");

long totalSize = raf.length();

int intTarget = Integer.parseInt(new String(target));

ret = new BinraySearch().search(raf, 0, totalSize, intTarget);

} finally {

if (raf != null) {

raf.close();

}



}



return ret;

}

}







実は、郵便番号でソートしたデータを作成するのが面倒だったりします。



公開されているデータを加工していろいろできそうですね。

[javascript]jsonがRFCになった。 2006/08/08
2008/01/28

jsonがRFCになっていた。

XMLでデータ交換って面倒くさいなぁと思っていたころ、巷ではAJAXがはやっていて、JSONのことをしったのが、2006年になってからだった。



javaメインで開発していて、ネットワークをjavaのオブジェクトをシリアライズさせてながす方法が便利だなぁと思っていて(RMIではなく)、クライアント(Java)ーサーバ(Java)で開発しているのに、データベースからデータを取得してJavaオブジェクトにしてまた、XMLになおして、そのXMLをねっワークを通して、クライアントでまたXMLからjavaオブジェクトにするのがかったるい。



が、XMLにするとデータの可搬性(といっていいのかな)が高まるのは承知しているのだが、面倒だなと感じていた。



jsonに注目している理由は、データ構造がシンプルであるということ、json自体にjavascriptのコードを埋め込めることができるからだ。



json自体にデータの操作方法記述してそれをクライアントに送り込むという方法。



これから開発してみたいなと考えているのは、jsonでデータを吐き出すサーバを用意して、そのサーバにAJAXで接続するクライアント、JavaWebStart(Rhinoを使う)で接続するクライアントアプリ、Flashで作成されたクライアントアプリとか、で接続するWebアプリ。javascirptの実行環境(インタープリタ)があればなんでもよいのかも。



でも、バイナリデータを乗せるのって無理だろうな。BASE64でエンコードするとかになるのかな。この辺調べてみないといけないな。

[java]windowsサービスをjavaで作成してみる 2006/08/03
2006/11/26

javaを利用したwindowsサービスの作成してみます。

javaを利用してwindowsサービスを手軽につくれるので、javaプログラマは楽できると思います。



この記事のヒントは、eclipseのTPTPプロジェクトでクライアントエージェントをインストール際に、windowsのコマンドで、manageserviceを使用していることがヒントになりました。



thinsitの記事



javaからwindowsのexeを作成するのには、exe4j製品です。exe変換にはその他にフリーのものもあるようです。探しみるのもよいでしょう。



無茶苦茶高い製品ではないので、思わずオンラインで購入してしまいました。

もちろんmac osxでも動作します。



exe4jwindowsサービスを作成する設定(executable type)ができます。



ここから手探り状態でやって単に動いたというレベルなので、技術的に正しくない可能性があります。



手順は、exe4jを利用してサービスを作成します。その際にexeの名前を、ACWinService.exeとします。



次にサービスexeを格納するディレクトリを作成します。

仮に%SERVICE_HOME%としますが、ディレクトリ(ファイル)の構成は%SERVICE_HOME%\bin\ACWinService.exe

とします。



コマンドプロンプトで下記のコマンドを利用して登録します。



manageservice add "サービス名" "%SERVICE_HOME%"





サービスを削除する場合は、



manageservice remove "サービス名"





最後に、windowsの管理コンソールでサービスの開始を行えばサービスとして起動します。

実験した環境は、windowsXP上で行いました。



サービスを削除する場合は、管理コンソールでサービスを停止させた状態で行います。



実用として採用するには、いくつか細かい点をクリアしないといけないかもしれません。

例えば、サービスの停止処理などです。



javaでwindowsサービスが手軽に作成できるというのはjavaプログラマにはうれしいことですね。