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

[java][ant][javadoc]doc-filesの中身をコピーしたい。 2006/07/26
2008/01/28

シナリオ:



jdkのjavadocコマンドには、-docfilessubdirsというオプションがあります。

doc-file サブディレクトリを再帰的にコピーするというものです。



antのjavadocタスクには-docfilessubdirsオプションに相当するものがないようです。

もしくは、doc-filesのファイルをコピーしてくれないようです。



javadocのドキュメントツリーにdoc-fileをコピーする必要があります。

というのも、javaのソース上に<a href="doc-files/xxx.txt">定義ファイルを参照する。</a>とリンクがjavadocから参照できるように設定されているからです。







単純にあとからファイルをコピーするという方法で解決してみる。





<!-- -doc-filesのコピー -->

<copy todir="${javadocdir}">

<fileset dir="${srcbase}/" includes="**/doc-files/*"/>

</copy>

[java][実装]文字の配列をカンマ区切りで出力してみる 2006/07/25
2006/11/26

シナリオ:



文字の配列をカンマ区切りで出力してください。







いろいろ実装方法はあると思う。

すぐに思いつくものをあげてみました。





public class TestFor {



/**

* シナリオ:文字の配列をカンマ区切りで出力します。

*/

public static void main(String[] args) {

String[] strings = { "a", "b", "c", "d", "e", "f", "g", "h", "i" };

System.out.println(pattern1(strings));

System.out.println(pattern2(strings));

System.out.println(pattern3(strings));

System.out.println(pattern4(strings));

}



public static String pattern1(String[] strings) {



StringBuffer buffer = new StringBuffer();

for (int i = 0; i < strings.length; i++) {

buffer.append(strings[i] + ",");

}

buffer.deleteCharAt(buffer.length() - 1);

return new String(buffer);

}



public static String pattern2(String[] strings) {



StringBuffer buffer = new StringBuffer();

for (int i = 0; i < strings.length - 1; i++) {

buffer.append(strings[i] + ",");

}

buffer.append(strings[strings.length - 1]);

return new String(buffer);

}



public static String pattern3(String[] strings) {



StringBuffer buffer = new StringBuffer();

for (int i = 0; i < strings.length; i++) {

buffer.append(strings[i]);

if (i < (strings.length - 1))

buffer.append(",");

}

return new String(buffer);

}



public static String pattern4(String[] strings) {



StringBuffer buffer = new StringBuffer();

String[] strings2 = new String[strings.length];



for(int i=0;i<strings2.length;i++)strings2[i] = ",";

strings2[strings2.length - 1] ="";



for (int i = 0; i < strings.length; i++) {

buffer.append(strings[i]).append(strings2[i]);

}

return new String(buffer);

}

}





[java]ランチャーを作成してアプリケーションを起動する 2006/07/21
2006/11/26

クラスローダ



javaアプリケーションを起動する際に、注意しないといけないのは昔からクラスパスだと思う。



アプリケーションによってはオープンソースのライブラリなど多く使用する場合、起動時に依存するライブラリについて全て暮らすパスを用意しないといけない。



個人的に、JAVA_HOMEにあるlib/extにライブラリを置いとくということはすすめないです。

というのは、《なんとなく動く》ということを引き起こしてしまいがちだからです。



よくあるのがjavaコマンドを使用する際にクラスパスを設定する方法です。

この場合、環境変数を設定して、プラットフォームごとのスクリプト(sh,bat)を用意するの面倒になります。



というわけで、下記のシナリオを考えてみました。



シナリオ:



アプリケーションとアプリケーションをスタートさせるランチャー部分を分ける。

ランチャー部分には環境変数XXX_HOMEなどの設定や、任意のクラスパスを設定したクラスローダを準備させる。

ランチャーは単純にjava -jar launcher.jarで動作させる。







これは軽量HTTPサーバのjettyのバージョンが5のソースコードを調べていたことが着想となります。





最終的には以下のようなjarの構成になります。

launcher.jar

application.jar

dependency.jar



アプリケーションの動作環境は下記のようなディレクトリ構成になります。



HOME/

lib/*.jar

conf/設定ファイル





ここから

開発していてはまった問題です。技術的な説明は調べている途中です。



ランチャー部分のjarと、アプリケーション部分のjarを分離させること。

アプリケーション部分のjarが依存しているjarをみつけることができないケースが発生する。



(1)最初のクラスローダ(ランチャーjar、アプリケーションjar)

(2)オリジナルのクラスローダ(依存しているjar)



問題は(1)のクラスローダで読み込んだクラスからは(2)で読み込んだクラスがみつからないということです。



javaではクラスの呼び出しが発生したときにClassNotFoundが発生します。

ですので、依存しているjarのクラスを実際に使用する段階になってはじめてClassNotFoundが発生します。



クラスローダを重ねていく(委譲していく?)際の注意は、クラスの依存の方向を常に後から読み込んだものは先に読み込んだものに依存するようにする

ということだと現在は自分の中で結論づけています。




ですので、



(1)最初のクラスローダ(ランチャーjar)

(2)オリジナルのクラスローダ(アプリケーションjar、依存しているjar)



というにします。





ではランチャーjarはアプリケーションjarをどのようによびだすかといいますと、クラスローダのloadClassメソッドを使用してクラスをロードして、

リフレクションを使用してアプリケーションを起動します。



(2)オリジナルのクラスローダを設定するときは、



Thread.currentThread().setContextClassLoader(myClassLoader);.





jettyではアプリケーション部分の読み込みは、invokeMainという処理を用意しています。



public static void invokeMain(ClassLoader classloader,String classname,String[] args)

throws IllegalAccessException,InvocationTargetException,NoSuchMethodException,ClassNotFoundException

{

Class invoked_class=null;

invoked_class=classloader.loadClass(classname);

Class[] method_param_types=new Class[1];

method_param_types[0]=args.getClass();

Method main=null;

main=invoked_class.getDeclaredMethod("main",method_param_types);

Object[] method_params=new Object[1];

method_params[0]=args;

main.invoke(null,method_params);

}





クラスローダの振る舞いはじっくり調べてみると必要がありそうですね。





参考リンク:

One-JARでアプリケーションの配布を単純化する

試行錯誤

jettyのサイト

サンのサイト javadocクラスローダの説明っぽい

[java][javassist]privateなメソッドにアクセスするもうひとつの方法 2006/07/19
2006/11/26

javassistを利用して、privateなメソッドにアクセスするもうひとつの方法です。



シナリオ:

下記のクラスのprivateなメソッドgetSにアクセスしたいとします。



public class A_A {



private String s = "hello";



private String getS(){

System.out.println("i am privte method!!");

return s;

}



}





方法は、javassistを利用して、メソッドを取得して、そのメソッドをクラスから削除して、アクセス修飾子を変更して、再度クラスにそのメソッドをaddするというまどろっこしい方法をとっています。





import java.io.IOException;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;



import javassist.CannotCompileException;

import javassist.ClassPool;

import javassist.CtClass;

import javassist.CtMethod;

import javassist.NotFoundException;

import javassist.bytecode.AccessFlag;



public class TestAccessPrivateMethod2 {



public static void main(String[] args) throws NotFoundException,

CannotCompileException, InstantiationException,

IllegalAccessException, SecurityException, NoSuchMethodException,

IllegalArgumentException, InvocationTargetException, IOException {



ClassPool pool = ClassPool.getDefault();



CtClass myCtClass = pool.get("A_A");

CtMethod ctMethod = myCtClass.getDeclaredMethod("getS");



// 既にあるメソッドを削除

myCtClass.removeMethod(ctMethod);



// アクセス修飾子をpublicに変更

ctMethod.getMethodInfo2().setAccessFlags(AccessFlag.PUBLIC);



// 修正したメソッドを追加

myCtClass.addMethod(ctMethod);



Class myClass = myCtClass.toClass();

Object my = myClass.newInstance();



// 何もしなかったら通常 Exception in thread "main" java.lang.NoSuchMethodException: A_A.getS()

Method exeMth = myClass.getMethod("getS", new Class[] {});

System.out.println(exeMth.invoke(my, new Object[] {}));



}

}

[java]for文 2006/07/19
2006/11/26

for文の書き方練習







for(int i=0, j=1, k=i;i<10 || j < 11;i++, j++, k=i+j){

System.out.println(i);

System.out.println(j);

System.out.println(k);

}



ソースコードの盗み方|悪態のプログラマ 2006/07/19
2006/11/26

ソースコードの盗み方|悪態のプログラマ



オープンソースコードをながめてて、ライブラリとして利用するには大きいすぎるし、ライセンス上はコピーしてもよさげだなぁ、と思うことがしばしばある。



そういう場合は、できる範囲で出典を明らかにして、コードを直で利用するようにしている。



大切なことはその知識、技術の《由来》を指し示すことかなと思う。



コードをチェックせずにつかえる、つかえないと即断するような大人にはなるまいまい。。。



オープンソースのコードを調べるのには、

http://www.koders.com/

がよさげ。

[java]classpath 2006/07/18
2006/11/26

Classpathクラス

実装参考は、jetty。



koders  Classpath.java





import java.io.File;

import java.io.IOException;

import java.net.MalformedURLException;

import java.net.URL;

import java.net.URLClassLoader;

import java.util.Arrays;

import java.util.Vector;



public class Classpath {



Vector<File> vector = new Vector<File>();



public Classpath() {

}



public Classpath(String initial) {

addClasspath(initial);

}



public boolean addClasspath(String component) {

if ((component != null) && (component.length() > 0)) {

try {

File f = new File(component);

if (f.exists()) {

File key = f.getCanonicalFile();

if (!vector.contains(key)) {

vector.add(key);

return true;

}

}

} catch (IOException e) {

}

}

return false;

}



public boolean addClasspath(File component) {

if (component != null) {

try {

if (component.exists()) {

File key = component.getCanonicalFile();

if (!vector.contains(key)) {

vector.add(key);

return true;

}

}

} catch (IOException e) {

}

}

return false;

}



public String toString() {

StringBuffer cp = new StringBuffer();

int cnt = vector.size();

if (1 <= cnt) {

cp.append(((File) (vector.elementAt(0))).getPath());

}

for (int i = 1; i < cnt; i++) {

cp.append(File.pathSeparatorChar);

cp.append(((File) (vector.elementAt(i))).getPath());

}

return cp.toString();

}



public ClassLoader getClassLoader() {

int cnt = vector.size();

URL[] urls = new URL[cnt];

for (int i = 0; i < cnt; i++) {

try {

urls[i] = ((File) (vector.elementAt(i))).toURL();

} catch (MalformedURLException e) {

}

}



ClassLoader parent = Thread.currentThread().getContextClassLoader();

if (parent == null) {

parent = Classpath.class.getClassLoader();

}

if (parent == null) {

parent = ClassLoader.getSystemClassLoader();

}

return new MyLoader(urls, parent);

}



private class MyLoader extends URLClassLoader {

String name;



MyLoader(URL[] urls, ClassLoader parent) {

super(urls, parent);

name = "MyLoader:[" + Arrays.asList(urls) + "]";

}



public String toString() {

return name;

}

}



}





[java]privateなメソッドにアクセスするもうひとつのやり方 2006/07/18
2006/11/26

private,protectedなメソッドにアクセスしたいときがある。



javaのコンパイル結果のバイトコードを操作する方法で考えてみたい。



javassist

を使用してみる。



インターネットにはjavassistを紹介したよいページがたくさんありますが、ちょいと自分でためしてみることにする。



このようなクラスがある。



public class A_A {



private String s = "hello";



private String getS(){

System.out.println("i am privte method!!");

return s;

}



}





getSメソッドにアクセスしたいのだが、アクセス修飾子がprivateである。



getSにアクセスするための別のメソッドを足してみるという方法をとってみる。





サンプルコードは以下



import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;



import javassist.CannotCompileException;

import javassist.ClassPool;

import javassist.CtClass;

import javassist.CtMethod;

import javassist.CtNewMethod;

import javassist.NotFoundException;



public class TestAccessPrivateMethod {



public static void main(String[] args) throws NotFoundException,

CannotCompileException, InstantiationException,

IllegalAccessException, SecurityException, NoSuchMethodException,

IllegalArgumentException, InvocationTargetException {



ClassPool pool = ClassPool.getDefault();



CtClass myCtClass = pool.get("A_A");



CtMethod newMethod = CtNewMethod.make("public String getS2() {"

+ " return getS(); }", myCtClass);



myCtClass.addMethod(newMethod);



Class myClass = myCtClass.toClass();

Object my = myClass.newInstance();



Method exeMth = myClass.getMethod("getS2", new Class[] {});



System.out.println(exeMth.invoke(my, new Object[] {}));

}

}







getS2というメソッドを作成して、追加している。





その他リンク



javassistチュートリアル



Javassist メモ

[java][classpath]プログラム上からのクラスパスの設定というかクラスローダの設定 2006/07/18
2006/11/26

毎回、アプリケーションを作成する際の悩みは、設定ファイルをどうするかです。



フレームワークにまかせるほどでもない場合。。。



アプリケーション起動時の引数にクラスパスを設定したり、環境変数に用意したり、-Dオプションを使うとかいろいろあります。



設定ファイルや、画像ファイルの読み込みに、



Xxxxx.getClassLoader().getResource



をよく使用してます。しかしこれだと、クラスパスが設定されてないと読めません。



では、



String classpath = System.getProperty("java.class.path")+File.pathSeparatorChar+"適当なパス";

System.setProperty("java.class.path", classpath);





でうまくいくのでしょうか。

この方法ではだめなようです。





クラスロードをセットしなおすのがよい方法のようです。





Thread.currentThread().setContextClassLoader(マイクラスローダー);







上記の方法を使用する場合は、



Thread.currentThread().getContextClassLoader().getResource(xxxx);





を使用するようにします。



オリジナルのクラスローダを使用して、アプリケーションで設定ファイルの位置を読み込むというのがいいかなぁとか思いました。

[コーディング全般]ソースコードの修正箇所に日付を入れることについて 2006/07/14
2006/11/26

これはNGだと思うこと。



ソースコードの修正箇所に日付しか入ってない。



たとえば、



// 8/23 XXXXに対応 S.Nakawaka

a = b + c;





こんな風にソースコードに修正コメントが入っていたら頭がくらくらする。



上記の例だと、最低でも年月日にしないとだめ。



それ以上に修正箇所に日付を入れること自体、よろしくないことだと思う。

その日付が後から意味をなすことなんて、ほとんどないと思うし、《場当たり》的すぎる。



ましてやソースコード管理用のツール(CVS等)を使用しているなら、中途半端に日付を入れなくてもいいのではないかだろうか。



コメントいれるならもっと説明を加えたほうがよいだろう。



おそらくこういったコードに対する修正というのは、あとから誰も手をつけれない《魔の領域》になる可能性が大だと思う。



この魔の領域は拡大してはじめてそのソースコードを廃棄してゼロからつくることになるのかもしれないのだろう。

[java]サーバープログラムをソケット使用して停止させる。 2006/07/13
2006/11/26

サーバプログラム(デーモン)を作成する際に、どうやって停止させようかと悩んだ。



ソケットを通して停止させるパターンのサンプルを書いてみた。



もとネタは、軽量HTTPサーバのJettyです。



jettyのorg.mortbay.startパッケージ、org.mortbay.stopパッケージを参考にしています。



まずmonitorを作成します。monitorはソケットを通して命令を受付て、System.exitを行います。



package scoketdecontrol.start;



import java.io.InputStreamReader;

import java.io.LineNumberReader;

import java.net.InetAddress;

import java.net.ServerSocket;

import java.net.Socket;



public class Monitor extends Thread {



static public String STOP_KEY = "stop.key";

static public int STOP_PORT = 8079;



ServerSocket serverSocket;



private Monitor() {



try {



// daemon thread

setDaemon(true);

serverSocket = new ServerSocket(STOP_PORT, 1, InetAddress

.getByName("127.0.0.1"));

if (STOP_PORT == 0) {

STOP_PORT = serverSocket.getLocalPort();

System.out.println(STOP_PORT);

}



} catch (Exception e) {



e.printStackTrace();



}



if (serverSocket != null) {

this.start();

} else {

System.err.println("WARN: Not listening on monitor port: "

+ STOP_PORT);

}

}



public void run() {

while (true) {

Socket socket = null;

try {

socket = serverSocket.accept();



LineNumberReader lin = new LineNumberReader(

new InputStreamReader(socket.getInputStream()));

String key = lin.readLine();



if (!eq(key, STOP_KEY))

continue;



String cmd = lin.readLine();



System.err.println("received command=[" + cmd + "]");



if (eq(cmd, "stop")) {

try {

socket.close();

} catch (Exception e) {

e.printStackTrace();

}

try {

serverSocket.close();

} catch (Exception e) {

e.printStackTrace();

}

System.exit(0);

} else if (eq(cmd, "status")) {

socket.getOutputStream().write("OK\r\n".getBytes());

socket.getOutputStream().flush();

}

} catch (Exception e) {

e.printStackTrace();

} finally {

if (socket != null) {

try {

socket.close();

} catch (Exception e) {

}

}

socket = null;

}

}

}



protected static boolean eq(Object object, Object object2) {

return object == null ? object2 == null : object.equals(object2);

}



public static void monitor() {

new Monitor();

}



public static void main(String[] args) {

Monitor.monitor();

}

}







つぎにmonitorを利用するメインのスレッドが動いているアプリケーションのサンプルです。

GUIをもつFrameにしてみました。



mainでmonitorを起動しています。



package scoketdecontrol.start;



import javax.swing.SwingUtilities;

import java.awt.BorderLayout;

import javax.swing.JPanel;

import javax.swing.JFrame;



public class TestMainForMonitor extends JFrame {



private static final long serialVersionUID = 1L;

private JPanel jContentPane = null;



public static void main(String[] args) {



Monitor.monitor();



SwingUtilities.invokeLater(new Runnable() {

public void run() {

TestMainForMonitor thisClass = new TestMainForMonitor();

thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

thisClass.setVisible(true);

}

});

}





public TestMainForMonitor() {

super();

initialize();

}



private void initialize() {

this.setSize(300, 200);

this.setContentPane(getJContentPane());

this.setTitle("JFrame");

}



private JPanel getJContentPane() {

if (jContentPane == null) {

jContentPane = new JPanel();

jContentPane.setLayout(new BorderLayout());

}

return jContentPane;

}



}







つぎに、停止命令を発行する別アプリケーションです。





package scoketdecontrol.stop;



import java.io.OutputStream;

import java.net.InetAddress;

import java.net.Socket;



import scoketdecontrol.start.Monitor;



public class Main {



int port = Monitor.STOP_PORT;;

String key = Monitor.STOP_KEY;



public static void main(String[] args) {

new Main().stop();

}



void stop() {

final String STOP_COMMAND = "stop";

try {

Socket s = new Socket(InetAddress.getByName("127.0.0.1"), port);

OutputStream out = s.getOutputStream();

out.write((key + "\r\n" + STOP_COMMAND + "\r\n").getBytes());

out.flush();

s.shutdownOutput();

s.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}







TestMainForMonitorを起動して、stopを起動するとFrameが終了します。



上記のサンプルでは、使用するポートを固定し、識別のためのkeyを固定にしてますが、これを可変にした場合、どこにその値をもたせるか悩みます。



jetty-5.1.1のソースでは、Syste.properyを使用していましたが、いまいちきめてにかけるような気がします。

[java][eclipse]VEでエラー 2006/07/13
2008/01/28

eclipseで、Visual Editorを使用していて下記のようなエラーログがでてきた。

特別なことをしたわけではない。





IWAV0168E Improper nesting of mark statements in Expression. Tried to pop or endMark to before current mark. Tried to go past Mark# 0.







グーグルさんにうかがったら、メーリングリストに解決方法がのっていた。



[news.eclipse.tools.ve] Re: VE





1) Open a VE and then close it. Make sure there are NO VE's open at all.

2) Clear the .log file (use the ErrorLog view and the "Delete Log" button).

3) Projects->Clean...->Clean ALL projects radio button

4) Wait for build to finish

5) Try opening the VE again.



news.eclipse.tools.veより







ProjectのCleanで解決した。

ProjectのCleanはごくごく初歩的でした

orz

[java]文法 ラベル 2006/07/11
2006/11/26

あまりラベルを使用したプログラムをみかけてない。



正直ラベルを無理にでも使用したい衝動にかられる時がある。



下記のソースはたんにABCDと印字するだけのものです。



public class TestLabel {



public static void main(String[] args) {



A: {

System.out.println("A");

B: {

System.out.println("B");

if(false) break B;

}



C:{

System.out.println("C");

E:{

if(true) break E;

}

if(false) break C;

}



D:{

System.out.println("D");

if(false) break D;

}

break A;

}

}

}



[java]jdk1.5 2006/07/10
2006/11/26

jdk1.5を使いたい訳。

XPathのパッケージが含まれている。

[java]プロファイラ 2006/07/06
2006/11/26

正直プロファイラを使いこなすのに時間をかけたくない。

結果を可視化して、直感的に理解できるのがいいなぁ



という意味でも、IDEとリンクしているものがうれしい。



さがすと、フリーでないものがおおくひかかるような気がする。



おためし版をためしたいのだが、フォームに入力するのが面倒だ。



で、ためしたもの

eclipse project のTPTPのプロファイラ

JProfilerの評価版



プロファイラとしてはJProfilerがよいと思う。

mac osx版もあることなので。



時間があったらその他のものもためしに使ってみたいが、

現時点では、JProfilerがいい感じだった。





その他

YourKit Java Profiler - YourKit Profiler for .NET - The profilers for Java and .NET professionals

[java][eclipse] TPTPのプロファイラ 2006/07/06
2006/11/26

The Eclipse Test & Performance Tools Platform (TPTP) Projectを使ってみた。
eclipseのプロジェクトであるのがうれしい。

でも、現時点で、osxに対応していない。プロファイラを使用するためには、eclipseとは別にアプリケーションをサービスとして、動かさないといけない。そのサービスアプリケーションのmac osx版がなかった。

簡単に使用した感想は、JProfilerの評価版を使用したあとだとちょっと使いづらい気がする。
JProfilerがさくさく動いたたので、ちょっとTPTPのプロファイラは使いづらい感じがした。

eclipse TPTP

[ThinkIT] 第1回:Eclipse TPTPのインストール (1/3)

OSX版がでれば、もっと使用してみたいのだが。。。

[java]これは絶対極悪コードだと思うぞ 2006/07/05
2006/11/26

下記のコードをみて、何も気づかなかったら、ちょいとあぶない。



protected static void a(){

String s = null;

if(s.equals("") || s == null){

System.out.println("s is empty.");

}

}





もちろん結果は、下記の通りである。



Exception in thread "main" java.lang.NullPointerException



nullの判定が効いていない。





正直Stringの扱いで

if(s == null || s.equals(""))

を書きたくない。



迷わず、ユーティリティパッケージを作成し、StringUtilsクラスを用意します。

それで、hasLenghtメソッッドを実装。



public static boolean hasLength(String s){

if(s == null || s.equals("")){

return false;

}

return true;

}



このユーティリティメソッドを使い回ししたほうが安全だと思う。

というか絶対そうする。





扱いたいStringオブジェクトにnullが入る可能性があることに耐えきれないなら、



public static String toNotNull(String s) {

if (s == null)

return "";

return s;

}



を作成して、



String s = null;

s = toNotNull(s);



したほうがよいよ思う。





jdk1.5を使用するとstatic importが使えるので、ユーティリティクラスを使用する際に、みためもすっきりになるし。

[java]System.gcについて 2006/07/04
2006/11/26

System.gcを使用するのに抵抗がある。

というのも、System.gcを使用するとアプリケーションにもたつきが発生するからだ。



System.gcを使用するためのタイミングをコントロールをできてばよいが、経験的にいえば、はじめから、System.gcの追加は予定されたコードではないケースが多い。



《場あたり的な》コードのSystem.gcは避けたほうがよいと思う。



といっても使わないといけない。状況になりつつある。どうしよう。





WebSphereべからず集



Javaの理論と実践: ガベージコレクションとパフォーマンス



GCのオプションの説明など