Top / Java / TIPS集

あ、これってべんりだなあって思ったコーディングのメモ。

AWSのLinuxで Mavenを使う

AWSのLinuxで、速攻でMavenでビルドしたくなって、しらべた内容。ついでにJavacを切り替えるやり方まで。

Logの制御

かつては Log4j がただ唯一の選択肢でしたが、どうやら乱立してるっぽい。そんなかでも多分コレがデファクト。

SLF4J経由でLogback。ついでにLombokでAnnotation。これで決まりかな。。

設定ファイルの制御

ResourceBundle?を使った設定ファイルの制御について。ResourceBundle?ってpropertiesファイルで設定できるのみだと思ってたんですけど、どうやら設定情報(key/value)を返却するクラスを実装する機構があって、さらにその設定情報をpropertiesファイルで記述できる機能もある、って構造なんですね。。

下記のサンプルは設定情報をクラスパス上のプロパティファイルで設定できるようにしつつ、ファイルがなかった場合はデフォルト値を適用する、なんてケース。よくあるケースですね。ここで、デフォルト値は DefaultResources? クラスというListResourceBundle?を拡張したクラスを使用しています。

package nu.mine.kino.resourcesamples;

import java.util.Enumeration;
import java.util.ResourceBundle;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Main {
    public static void main(String[] args) {
        new Main().execute();

    }

    // sample.properties をクラスパスから探して、なかったらデフォルト値を適用する。
    // デフォルト値は、nu.mine.kino.resourcesamples.DefaultResources クラスで定義している。
    private void execute() {
        String propertyFile = "sample";
        ResourceBundle bundle = null;
        try {
            bundle = ResourceBundle.getBundle(propertyFile);
            doSettings(bundle);
        } catch (java.util.MissingResourceException e) {
            String message = "設定ファイルが存在しません。必要ならクラスパス上に {}.propertiesを配置してください。({})";
            log.warn(message, propertyFile, e.getMessage());
            doSettings(ResourceBundle.getBundle(
                    "nu.mine.kino.resourcesamples.DefaultResources"));
        }
    }

    private void doSettings(ResourceBundle bundle) {
        Enumeration<String> keys = bundle.getKeys();
        while (keys.hasMoreElements()) {
            String key = keys.nextElement();
            log.debug("key[{}]:{}", key, bundle.getString(key));
        }
    }
}
package nu.mine.kino.resourcesamples;

import java.util.ListResourceBundle;

public class DefaultResources extends ListResourceBundle {
     @Override
    protected Object[][] getContents() {
        return new Object[][] { { "param1", "default value1" },
                { "param2", "default value2" } };
    }
 }

sample.properties

param1=property value1
param2=property value2

ファイルパスの操作(Java7版)

ファイルのパスをつなげるようなときに、もちろんStringでつなぐとかは論外として、、いままでは

File base = new File("/tmp/base");
new File(new File(base, "more1"), "more2");

こんな風にくるんでくるんで、みたいな処理をしていたのが、

Paths.get("/tmp/base","more1","more2").toFile();

このように可変長引数を使ってつなげるようになりました。いまどきの言語だと当たり前なんですけど、Javaもキレイになりました。。

バイナリファイルをbyte配列に変換する(Java7版)

便利な世の中になりました。

try {
    byte[] bytes = Files.readAllBytes(Paths.get("パス"));
} catch (IOException e) {
    // TODO 自動生成された catch ブロック
    e.printStackTrace();
}

自前で書く必要なくなっちゃいました。AutoClosable?で自動でStreamをcloseしてくれてるし。

ある配列に配列を追加して新しい配列を作る

Object[] newArray = ArrayUtils.addAll(array1, array2);

ある配列に単一のObjectを追加して新しい配列を作る

Object[] array = ...;
Object element = ...;
Object[] newArray = ArrayUtils.add(array, element);

Commonsのデリミタで分割するヤツ

String[] splits = StringUtils.splitPreserveAllTokens(target, delimiter);

StringBuffer?でカンマつなぎしたとき、最後のカンマはいらない件

StringBuffer buf = new StringBuffer();
for (String str : array) {
    buf.append(str);
    buf.append(',');
}
buf.deleteCharAt(buf.length() - 1);
return new String(buf);

わすれがちな、Dateの比較 before/after

Date base = new Date();
Date target = base;
System.out.println(base.after(target));  //おなじ日はfalse
System.out.println(base.before(target)); //おなじ日はfalse
System.out.println(base.before(DateUtils.addDays(target, 1)));  // targetより前か → true
System.out.println(base.after(DateUtils.addDays(target, -1)));  // targetより後か → true

結果は

false
false
true
true

バイト列を表示する。

テキストファイルが文字化けしてて、どんなバイナリの値なんだろって確認したいときとかに、バイト列を取得し16進で画面表示するには以下のようにします。

private void printByte1(byte[] bytes) {
    String hexString = toHexString(bytes);
    System.out.println(hexString);
}

// byte[] -> 16進文字列へ変換
private String toHexString(byte[] b) {
    StringBuffer hexString = new StringBuffer();
    String plainText = null;
    for (int i = 0; i < b.length; i++) {
        plainText = Integer.toHexString(0xFF & b[i]);
        if (plainText.length() < 2) {
            plainText = "0" + plainText;
        }
        hexString.append(plainText);
        hexString.append(" ");
    }
    return new String(hexString);
}

String#formatメソッドを使えば書式の指定で対応出来ますね。。

private void printByte2(byte[] bytes) {
    for (byte b : bytes) {
        System.out.printf(String.format("%1$x ", b));
    }
    System.out.println();
}

そもそもどうやってバイナリでファイル読むのっていうのは下記の通りやればできます。

ファイルをバイナリで読み込む。。

定番はコレですかね

String baseDirStr = "/tmp";
String fileName = "data.txt";
FileInputStream in = new FileInputStream(new File(baseDirStr, fileName));
int ch;
while ((ch = in.read()) != -1) {
    // System.out.print(Integer.toHexString(ch) + " ");
    System.out.printf(String.format("%1$x ", ch));
}

こういうのもある。。

RandomAccessFile accessFile = null;
try {
    File file = new File(baseDirStr, fileName);
    accessFile = new RandomAccessFile(file, "r");
    int length = (int) file.length();
    byte[] bytes = new byte[length];

    accessFile.read(bytes);
    printByte1(bytes);
    printByte2(bytes);
} finally {
    if (accessFile != null) {
        accessFile.close();
    }
}

RandomAccessFile? にはバイナリファイルを readDouble とかやって型を指定して取得できたりするので、まさにバイナリをファイルを読むときにはこっちが良さそうです。

テキストファイルをバイナリで読んでみるときとかは前者が良さそうです。

数値の文字列フォーマット変換

doubleとかの数値をフォーマッティングした文字列を取得するメソッドです。こんな感じ。

String.format("%1$,.0f" + " KB中、" + "%2$,.0f"+ " KB処理しました。" + "%3$,.0f" + " %%処理完了",
      a / 1024.0, b / 1024.0,  c / 1024.0);

なかのプレースホルダは、それぞれ

%1$ -> a / 1024.0
%2$ -> b / 1024.0
%3$ -> c / 1024.0

って可変長の引数に対応。さらに

,.0f

で、「3ケタでカンマ区切り」「小数点以下は0ケタ」って意味。ちなみにフォーマット内で%を使う場合は、上記のように%%ってエスケープ。。

java.util.Date -> int

ついでに。。

Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());  <-2009/6/8に実行した
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(year);
System.out.println(month);
System.out.println(day);

などで、Dateをintに変換可能です。

2009
5
8

Monthだけは日本人の感覚と一つ値がずれるので注意。

同様に、Calendar#set(int year, int month, int date) 、、具体的には

Calendar calendar = Calendar.getInstance();
calendar.clear(); <-下で時分を指定しないから、clearしないと現在時刻が保持されちゃうので。
calendar.set(year, month, day);
Date time = calendar.getTime();

とすることで、int -> java.util.DateもOKそうですね。

Calendar (Java 2 Platform SE 5.0)

String -> java.util.Date

文字列からjava.util.Dateを生成する方法ですが、

Date parseDate = new SimpleDateFormat("yyyy/MM/dd").parse(date);
System.out.println(parseDate);

などとやればできますが、Commons Langの org.apache.commons.lang.time.DateUtils? をつかえばいくつかのフォーマットを同時に指定してDateを生成することができます。

Date date = DateUtils.parseDate(dateStr, new String[] { "yyyyMMdd","yyyy/MM/dd" });

引数のパターンで順々にdataStrをパースして、Dateクラスを生成してくれます。

java.util.Date -> String

逆に、Dateから文字列を取得する方法。

String nowDateString = DateFormatUtils.format(nowDate, "yyyy/MM/dd");
System.out.println(nowDateString);

フォーマットの指定の仕方によって、年を取得、月を取得、日付を取得、とかができますね。

「日」より下を切り捨てたDateを取得する

日付だけ管理したいんだよなぁってときがよくありますが、DateUtils? はその要求に応えてくれます。

Date nowDate = new Date();
System.out.println("Today: " + nowDate);
// 「日」より下を切り捨て
Date dateTruncate = DateUtils.truncate(nowDate, Calendar.DAY_OF_MONTH);
System.out.println("Today: " + dateTruncate + " <-「日」より下を切り捨て");
// 「月」より下を切り捨て
dateTruncate = DateUtils.truncate(nowDate, Calendar.MONTH);
System.out.println("Today: " + dateTruncate + " <-「月」より下を切り捨て");
// 「年」より下を切り捨て
dateTruncate = DateUtils.truncate(nowDate, Calendar.YEAR);
System.out.println("Today: " + dateTruncate + " <-「年」より下を切り捨て");

結果は以下の通り:

Today: Mon Jun 08 10:00:54 JST 2009
Today: Mon Jun 08 00:00:00 JST 2009 <-「日」より下を切り捨て
Today: Mon Jun 01 00:00:00 JST 2009 <-「月」より下を切り捨て
Today: Thu Jan 01 00:00:00 JST 2009 <-「年」より下を切り捨て

stackTraceを取得する

標準エラーに出力するならe.printStackTrace?()ですが、文字列として取得したい場合は、ストリームにスタックトレースを出力します。

private String exception2String(Exception e) {
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  e.printStackTrace(new PrintStream(baos));
  return baos.toString();
}

文字コードとか大丈夫だっけとかありますが、こんな感じでできます。

プロパティファイルから、複数のデータを配列で取得する

Java Collections Framework の org.apache.commons.collections.ExtendedProperties? を用いると、

  • hoge.properties
    data=hoge@fuga.com
    data=hogehoge@fuga.jp
    などという複数のデータを配列で取得することができます。このファイルはクラスパスが通った場所に置いておきます。

上記データにアクセスするサンプルコードは以下の通り。

ExtendedProperties props = new ExtendedProperties();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream resourceAsStream = loader
        .getResourceAsStream("hoge.properties");
InputStream in = new BufferedInputStream(resourceAsStream);
props.load(in);
String[] dataArray = props.getStringArray("data");

などとしてデータを取り出すことができます。

パイプされた標準入力を取得する

そういえばJavaって標準入力をどう受け取るんだっけ??ということで調べたメモ。パイプされた標準入力をechoするサンプル。

package nu.mine.kino.mail.utils;

import java.io.BufferedInputStream;
import java.io.IOException;

public class Echo {
    public static void main(String[] args) {
        BufferedInputStream in = new BufferedInputStream(System.in);
        try {
            int size = in.available();
            byte[] bytes = new byte[size];
            in.read(bytes);
            System.out.println(new String(bytes));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

結果はたとえば

[hogehoge@www Test]$ ls  |  java -cp lib/sample.jar nu.mine.kino.mail.utils.Echo
build.win.properties
build_jar.xml
classes
lib
source

[hogehoge@www Test]$

lsの結果を受け取り、echoできました。

  • シェルからJavaを呼び出すときもOK
    [hogehoge@www Test]$ cat sample.sh
    export JAVA_HOME=/opt/jdk1.5.0_12
    export LIBDIR=lib
    export CLASSPATH=${LIBDIR}/sample.jar
    java nu.mine.kino.mail.utils.Echo
    [hogehoge@www Test]$ ls  |  ./sample.sh
    build.win.properties
    build_jar.xml
    classes
    lib
    sample.sh
    source
    
    [hogehoge@www Test]$

ちなみに標準入力を待ち受けてるときに入力完了を通知するための終了文字は Platform-Specific Details: The End-of-Input Character に書いてありました。

参考

Apache Axis 1.4のSSL処理にはバグがありそう

詳しくはまた時間がとれたらにしますが、Basic認証するプロキシサーバ越えでSSL通信するときのAxisの挙動がヘン。Axisが生成する Proxy-Authorizationヘッダの改行コードがおかしい模様。HTTPSの仕様的には、ほんとは\r\nが推奨されるらしいけど\nしかないため、まれに解析に失敗するプロキシサーバがあるようです。

BigDecimal?のコンストラクタにdoubleは使わないほうがいい

ちゃんと数値計算しようと思って、doubleとかの代わりにBigDecimal?を使ってたんだけど、やっぱ誤差が出ちゃう。何でだろーと思ってたら、BigDecimal?のコンストラクタでStringでなくdoubleを使ってたからでした。doubleを引数に取るコンストラクタの方は、引数自体が誤差が出ちゃってるから、正確な計算ができないって事ですね。素直にStringを使ったところ、ビシっと計算が合いました。

って、よく見たらJavaDoc?にも書いてありますね。

classesディレクトリを一括で削除するワンライナー

rm -rf `find ./ -type d -name classes`

ファイル <-> byte[]に変換するUtil

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;

/*******************************************************************************
 * Copyright (c) 2007 Masatomi KINO. All rights reserved. 
 * $Id$
 ******************************************************************************/
public class Utils {
  public static byte[] file2Byte(String file) throws IOException {
    InputStream in = null;
    byte[] pix = null;
    try {
      in = new FileInputStream(file);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      byte[] b = new byte[1024];
      int j;
      while ((j = in.read(b)) != -1) {
        baos.write(b, 0, j);
      }
      pix = baos.toByteArray();
    } finally {
      if (in != null) {
        try {
          in.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    return pix;
  }

  public static void byte2File(byte[] b, String outputFile)
      throws IOException {
    BufferedOutputStream stream = null;
    try {
      File file = new File(outputFile);
      FileOutputStream fstream = new FileOutputStream(file);
      stream = new BufferedOutputStream(fstream);
      stream.write(b);
    } finally {
      if (stream != null) {
        try {
          stream.close();
        } catch (IOException e1) {
          e1.printStackTrace();
        }
      }
    }
  }
} 

整数を16進や2進で表示する

System.out.println(Integer.toHexString(15));
System.out.println(Integer.toBinaryString(15));

実行結果は以下の通り。

f
1111

Base64でエンコードする

Base64とはE-mailなどバイナリデータを使用できない環境で、バイナリデータを扱うためのエンコード方式です*1。バイナリデータをテキストデータに可逆変換します。

Commons Codecを用いれば、バイナリデータを簡単にBase64エンコード可能です。

public class Base64Main {
  public static String encode(String input) {
    byte[] bs = Base64.encodeBase64(input.getBytes(), false); 
                // booleanはある単位で改行を入れるかどうか
    return new String(bs);
  }

  public static void main(String[] args) {
    String string = encode("kino");
    System.out.println(string);
  }
}

実行結果は以下の通り

a2lubw==

簡単ですね。引数にbyte[]を取るので、バイナリファイルからbyte[]を作成し、メソッドに渡してあげればバイナリファイルも簡単にエンコード可能なわけです*2

ちなみにこのCodecライブラリは下に出てくる byte[] -> HexString?な変換をするメソッドなどもあります。

 new String(Hex.encodeHex(b));

とすればOKです。

あ、よく見たらSHA-1でハッシュするメソッドもありました。

System.out.println(DigestUtils.shaHex("kino".getBytes()));

でしたと同じ結果を得ることができます。実行結果は以下の通り。

c108a2616c020deeb71df83906f039e3dfcc6752

おなじですね。

SHA1でハッシュ化

SHA-1はパスワードのハッシュ化などに使われるアルゴリズムです*3

 public static String encrypt(String input) {
   try {
     MessageDigest md = MessageDigest.getInstance("SHA1");
     md.update(input.getBytes("MS932"));
     return toHexString(md.digest());
   } catch (NoSuchAlgorithmException e) {
     e.printStackTrace();
   } catch (UnsupportedEncodingException e) {
     e.printStackTrace();
   }
   return null;
 }
 private static String toHexString(byte[] b) {
   StringBuffer hexString = new StringBuffer();
   String plainText=null;
   for (int i = 0; i < b.length; i++) {
     plainText = Integer.toHexString(0xFF & b[i]);
     if (plainText.length() < 2) {
       plainText = "0" + plainText;
     }
     hexString.append(plainText);
   }
   return new String(hexString);
 }
 public static void main(String[] args) {
   String string = encrypt("kino");
   System.out.println(string);
 }

実行結果は以下の通り。

c108a2616c020deeb71df83906f039e3dfcc6752

SHA1でハッシュ化できました。MessageDigest?.getInstance("SHA1");の「SHA1」を「MD5」にすればMD5でハッシュ化できます。

java.lang.ThreadLocal?について

thlocal.set( new LegacySystemImpl2());
thlocal.set( new LegacySystemImpl());
LegacySystem impl = (LegacySystem) thlocal.get();

などと同じ型のクラスをセットした場合、どうも上書きになるっぽい。ソースを調べた分けじゃないけど。。

http://www.hyuki.com/dp/dpinfo_ThreadSpecificStorage.html#i1

Eclipseのヒープとタスクマネージャの関係

Eclipseのヒープサイズと、タスクマネージャのjavawのメモリ使用量の関係。Eclipseのヒープが

1024M中の500M

とかなってる場合、1024Mの方がタスクマネージャ上に表示される。だから実際どれだけjavaプロセスがメモリを使用しているかはOSからはわからないみたいですね。

EclipseでCVSのブランチを取り扱う。

CVSのトランク(メインっていう?)とブランチをマスターしたくちょっと触ってみました。

まずはバージョンとしてタグ付け。タグ名はたとえば

V20061026_01

とする。次にブランチの作成。チーム >> ブランチ より作成。ブランチ名はたとえば

V20061026_01_BRANCH

とする。そのとき、ブランチをマージするための開始点のタグも併せて登録される。これは自動的に

Root_V20061026_01_BRANCH

というタグ名になる。メインのモジュールにブランチをマージするには、開始点ってのが重要みたいだ。

ここまででCVSには上の3つのタグ

V20061026_01                メイン側
V20061026_01_BRANCH         ブランチ側
Root_V20061026_01_BRANCH    メイン側

ができる。

Eclipseプロジェクトに、メインとブランチ両方落としておく。ブランチの方はプロジェクト名を_branchとかつけとく。

Eclipseで各プロジェクトでソースを修正してコミットする。

メイン側はバージョンは   1.1 -> 1.2 
ブランチ側はバージョンは 1.1 -> 1.1.2.1

となるみたい。

さあ、ブランチからメインへマージします。メインのプロジェクトで、チーム >> マージを選択し

マージされるバージョン(終了タグ) をブランチのバージョン   i.e. V20061026_01_BRANCH 
共通基本バージョン(開始タグ)     をさっきの開始バージョン i.e. Root_V20061026_01_BRANCH

と選択すると、左がメイン、右がブランチのソース比較画面が現れます。適宜Updateすればブランチのソースがメインに取り込まれます。後はメインにコミットすれば、ブランチ分のマージは完了です。

※これはローカルコピーに、開始タグと終了タグの差分を取り出すって事かな?実際ブランチ側でマージしようとしたら、V20061026_01_BRANCH とV20061026_01_BRANCH には差分がないっていう当たり前の表示になってしまった。

実行中のメソッド名を取得する。

new Throwable().getStackTrace()[0].getMethodName();

何故これでメソッド名が取れるんだろう。。。でもうまくいきますねー

byte配列をバイナリに変換する。

逆に、byte[]をファイルに書き出す方法

public static void write(byte[] b) {
  BufferedOutputStream stream = null;
  try {
    File file = new File("after.bin");
    FileOutputStream fstream = new FileOutputStream(file);
    stream = new BufferedOutputStream(fstream);
    stream.write(b);
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    if (stream != null) {
      try {
        stream.close();
      } catch (IOException e1) {
        e1.printStackTrace();
      }
    }

  }
}

バイナリファイルをbyte配列に変換する

Webサービスでバイナリをアップするときなどは、byte[]で渡すみたいです。

public byte[] createBin() {
  InputStream in = null;
  byte[] pix = null;
  try {
    in = new FileInputStream("before.bin"); <-バイナリファイル
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] b = new byte[1024];
    int j;
    while ((j = in.read(b)) != -1) {
      baos.write(b, 0, j);
    }
    pix = baos.toByteArray();
  } catch (IOException e) {
    e.printStackTrace();
  } finally {
    if (in != null) {
      try {
        in.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
  return pix;
}

Apache Axisで、プロキシーを越える。

Apache Axisを使ったWEBサービスのProxyクライアントで、HTTPのプロキシー(ややこしい)を越える方法を探していました。

いやあEclipseでソースにブレークポイントつけまくって、探したー。。。。どうやら、

 org.apache.axis.components.net.DefaultHTTPTransportClientProperties

てクラスが、なにやらプロパティをセットしていて、そこがAxisの環境設定値になってる模様。そしてこのクラスは、org.apache.axis.AxisProperties?というjava.util.Properties みたいなのを使っている。ソースを見ると、httpプロキシを越えるためのキー値は

AxisProperties.setProperty("http.proxyHost", "proxyのサーバ名");
AxisProperties.setProperty("http.proxyPort", "proxyのポート番号");
AxisProperties.setProperty("http.proxyUser", "xxxx");
AxisProperties.setProperty("http.proxyPassword", "xxxx");

越えた。。キター。。。

Apache Axisで、自己署名証明書を使用してSSLさせる方法

すげー

HSQLDBの起動、フロントエンドの起動

  • データベースサーバの起動
    java -cp /opt/hsqldb/hsqldb.jar org.hsqldb.Server 
      -database /tmp/bd -port 9001 -system_exit=true
  • フロントエンドの起動
    java -cp lib/hsqldb.jar org.hsqldb.util.DatabaseManager

ブラウザのCookieを手軽に参照する

JavaのTIPではないけど、J2EE開発中によく使うので。ブラウザのURLに

javascript:document.cookie;

と入力すると、クライアントのCookie(今開いているサイトに対するもののみ)を参照できます。

Jakarta Commons Langのいろいろ

Jakarta Commons LangはJavaのライブラリだけでは足りないようなユーティリティ的な機能を提供するクラスライブラリです。たとえば、こんな感じで使うことができます。

  • toString()を実装するときに使う
    public String toString() {
      return new ToStringBuilder(this)
        .append("id", getId())
        .append("title",getTitle())
        .toString();
    }
    とかで
    nu.mine.kino.rss.hibernate.Rss@33441dfb[id=10,title=hoge]
    と出力できたり、
  • equals()を実装するときに使う
     public boolean equals(Object other) {
       if (!(other instanceof Rss)){
         return false;
       }
       Rss castOther = (Rss) other;
       return new EqualsBuilder()
            .append(this.getId(), castOther.getId())
            .isEquals();
     }
    とかで、getIdが同じならtrue、なんてことができたりします。ちなみにappendはどんどん後ろに追加することができます。これは便利!!

フィールドをカプセル化する

設定ファイルとかの場所がフィールドに定数で埋め込まれていて、単体テストのときだけそのパスを変えたいなあなんてときがあります。最近はDIとかで変えれるようにするのがはやってますが、DIコンテナなども使ってないときなどはどうすればよいでしょうか。

例としてテスト対象クラスがHogeだとして

public class Hoge{
  private String path ="hoge/hogehoge.properties";
  .....
  public void exe(){
    new File(path).....;
  }
}

なんてのがあるとき、どうすればよいかってことです。

この場合、まずはソースを変更して単体テストしやすくします。そのソースの変更はEclipseなどを用いて、きっちりやる、と。やる手順は

  • フィールドへのアクセスをgetter経由にする(getterはデフォルトやprotectedにする)
  • 単体テスト時、anonymousクラスにして、そのgetterをoverrideして、値を変更する

具体的には上のクラスは以下のように変更します。

  • フィールドへのアクセスをgetter経由にする(getterはデフォルトやprotectedにする)
    public class Hoge{
      private String path ="hoge/hogehoge.properties";
      .....
      public void exe(){
        new File(getPath()).....;
      }
      
      String getPath(){
        return path;
      }
    }
  • 単体テスト時、anonymousクラスにして、そのgetterをoverrideして、値を変更する
    public class HogeTest{
      private Hoge hoge;
      .....
      public void testExe(){
        hoge=new Hoge(){
          String getPath(){
            return "新しいパス";
          }
        };
        hoge.exe();.......
      }
    }
    こうするとパスを変更することができます。気になるのは
  • フィールドを公開しちゃってる(デフォルトはパッケージ内、protectedはさらにサブクラスも)
  • ソースを変更している
  • anonymousのクラスに対するテストが、元のクラスのテストとしちゃってよいの?

ってところですね。。ソースの変更に関してはEclipseなどで論理的に安全に行うことはできますが。手順は、

変数を参照している箇所(new File(path)の"path")を選択して、リファクタリング >> フィールドのカプセル化

定数がフィールドにすらいない場合は、上の前にさらにこれをやる

変数を選択して、 リファクタリング >> ローカル変数を フィールドに変換

プレースホルダを使う

MessageFormat?を使うと、{0}のようなプレースホルダを使うことができます。

SimpleDateFormat yyyy = new SimpleDateFormat("yyyy");
SimpleDateFormat MM = new SimpleDateFormat("MM");
SimpleDateFormat dd = new SimpleDateFormat("dd");
Date selectedDate = new Date();
String year = yyyy.format(selectedDate);
String month = MM.format(selectedDate);
String date = dd.format(selectedDate);
Object[] dateArgs = { year, month, date };
MessageFormat form = new MessageFormat("{0}年{1}月{2}日"); <-プレースホルダ
System.out.println(form.format(dateArgs));

実行結果は

2005年03月18日

e.printStackTrace?()の文字列を取得する、出力先を変更する

例外のインスタンスをexとして

StringWriter stringWriter = new StringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
String message = stringWriter.getBuffer().toString();

WSAD5.1.2でサーバの設定が格納されているディレクトリ

${WORKSPACE}\.metadata\.plugins\com.ibm.wtp.server.core\configs

プロクシ認証などなど

-Dhttp.proxyHost=[プロクシサーバ名] -Dhttp.proxyPort=[ポート番号]
-Dhttp.proxyUser=[UserID] -Dhttp.proxyPassword=[password]

privateなフィールドにアクセスする

たとえばインスタンス名instance のフィールド(Stringで変数名aField)にアクセスするには

Class clazz = instance.getClass();
Field field = clazz.getDeclaredField("aField");
field.setAccessible(true);
String answer = (String)field.get(instance));

で取得できる。ここが参考になりました。

<%@ page %> タグ内の意味。

たとえば

<%@ page contentType=text/html; charset=EUC-JP" pageEncoding="Shift_JIS" %>

とした場合、pageEncodingがソースコードの文字コード、contentTypeで指定できるのが出力文字コードです*4。この場合はソースをShift_JISで書いて、EUC-JPで出力するってことですね。

key=value&...の形式ではないPOSTリクエストの電文を取得する方法

requestからReaderを取得して、そこから文字を取得すればよいわけですね。

BufferedReader reader = request.getReader();
StringBuffer buffer = new StringBuffer();
String line = null;
while ((line = reader.readLine()) != null) {
  buffer.append(URLDecoder.decode(line));
  buffer.append("\n");
}
String reqMsg = new String(buffer);

DOM2String

ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
  TransformerFactory tfactory = TransformerFactory.newInstance();
  Transformer transformer = tfactory.newTransformer();
  transformer.setOutputProperty("encoding", encoding);
  transformer.transform(new DOMSource(document), new StreamResult(out));
} catch (TransformerConfigurationException e) {
  logger_.error(e);
} catch (TransformerException e) {
  logger_.error(e);
}
return out.toString();

String2DOM

例外処理はとりあえずおいといて

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
StringReader stringReader = new StringReader(str); <- str: xmlの文字列
InputSource inputSource = new InputSource(stringReader);
return builder.parse(inputSource); <- Document

ファイル書きだし(バイナリで)

private void streamToFile(InputStream in, File file) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] b = new byte[1024];
    int j;
    while ((j = in.read(b)) != -1)
        baos.write(b, 0, j);
    byte[] pix = baos.toByteArray();
    write(pix, file);
}
private void write(byte[] b, File file) {
    BufferedOutputStream stream = null;
    try {
        FileOutputStream fstream = new FileOutputStream(file);
        stream = new BufferedOutputStream(fstream);
        stream.write(b);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }

    }
}

ファイル書きだし(テキスト)

public boolean write(String log) {
  BufferedWriter writer = null;
  try {
    //第二引数がtrueなら追記、falseなら上書き。
    writer = new BufferedWriter(new FileWriter("./log.txt", true));
    writer.write(log, 0, log.length());
    writer.newLine();
  } catch (FileNotFoundException e) {
    e.printStackTrace();
    return false;
  } catch (IOException e) {
    e.printStackTrace();
    return false;
  } finally {
    try {
      if (writer != null) {
        writer.flush();
        writer.close();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  return true;
}

あるディレクトリからの相対パスを計算して、他のディレクトリに連結したい。

うまく説明できませんが、別のディレクトリに同じパス構成をコピーしたいときなどに使用できると思います。

 //たとえば "c:/temp", "c:/temp/fuga/aaaaa.txt", "d:/huge/hoho" に対して
 // d:/huge/hoho/fuga/aaaaa.txt としたい
 public static String trimAndConcat(String base, String path, String other) {
   URI uriBase = new File(base).toURI();
   URI uriPath = new File(path).toURI();
   URI uriRelativised = uriBase.relativize(uriPath);
   return new File(other, uriRelativised.toString()).getAbsolutePath();
 }

URLからファイルパスへ。

URLクラスからそのファイルまでのパスを取得する方法です。

String path = new File(url.getPath()).getPath();

とやることで、

URL : file:/C:/hoge/config/hoge.txt
File: C:\hoge\config\hoge.txt

に変換ができます。*5*6

(たとえば)拡張子がxmlのファイルをリストする

File mainDirectory = new File([ディレクトリ]);
String[] files = mainDirectory.list(new FilenameFilter() {
  public boolean accept(File dir, String name) {
    if (name.endsWith("xml")) {
      return true;
    }
    return false;
  }
});

filesが拡張子がxmlのファイル(ファイル名、の配列)

拡張子より後ろの文字列を取得する

String hoge="aaa.xml";
hoge.substring(hoge.lastIndexOf('.'));

で.xmlが取得できる。

拡張子より前の文字列を取得する

String hoge="aaa.xml";
hoge.substring(0, hoge.indexOf('.'));

でaaaが取得できる。/etc/aaa.xml.bakとかには使えないけど。。

Javaから、WEBブラウザを起動(Windowsのみ)

try {
	Runtime.getRuntime().exec(
		new String[] {
			"rundll32.exe",
			"url.dll,FileProtocolHandler",
			"http://jp.sun.com/" });
} catch (IOException e) {
	e.printStackTrace();
}

SWTから起動したい方は こちら

WEBアプリケーションのルートを取得してパスを生成する

String root_path = this.getServletContext().getRealPath("/");
String file_path = root_path + "hoge.dat"

DOMオブジェクトをファイルに書き出す

try {
	TransformerFactory tfactory = TransformerFactory.newInstance();
	Transformer transformer = tfactory.newTransformer();
	//			transformer.setOutputProperty("encoding", "EUC-JP");
	transformer.transform(new DOMSource(document), <-document : XMLのDOMです
	//new StreamResult(System.out)); <- こっちだとコンソール出力
	new StreamResult(new FileOutputStream(new File(output))));

} catch (TransformerConfigurationException e) {
	e.printStackTrace();
} catch (TransformerFactoryConfigurationError e) {
	e.printStackTrace();
} catch (TransformerException e) {
	e.printStackTrace();
} catch (FileNotFoundException e) {
	// TODO 自動生成された catch ブロック
	e.printStackTrace();
}

JSPやServletのOutにDOMを書き出したい。

public void write(HttpServletRequest request, HttpServletResponse response) {
	response.setContentType("text/xml; charset=UTF-8");
	try {
		PrintWriter out = response.getWriter();
		transformer.transform(new DOMSource(getDocument()),
		new StreamResult(out));
	} catch (IOException e) {
	} catch (TransformerException e) {
	}
}

Servletで、コンテンツタイプを指定する。

response.setContentType("text/xml; charset=UTF-8");

プロキシ越えとか、HttpConnection?

//プロキシ認証の記述方法
BASE64Encoder encoder = new BASE64Encoder();
urlConnection.setRequestProperty(
 "Proxy-Authorization",
 "Basic " + encoder.encode((userid + ":" + password).getBytes()));

javaの実行

>java -classpath ".;lib;classes;lib\log4j-1.2.8.jar;swt.jar" kino.swt.JavaDocSearchForm
>java -classpath ".;lib;classes;lib\log4j-1.2.8.jar;swt.jar;lib\kino_javadocsearch.jar" 
 kino.swt.JavaDocSearchForm
>java -classpath "クラスパス(セミコロン区切り)" クラス

このサイトよいかも。

Javadoc書き方

@see	UserInformationManager#getUserInformation(String)

とすると、リンクが張られる。

{@link	InformationController	InformationController}のファイル読み込みの実装です。

とか

{@link クラス名#メンバ名 表示テキスト}

なんて使い方もできる

コレクションを配列に変換

type: 型
collection: コレクションクラス
(type[]) collection.toArray(new type[collection.size()]);

配列をコレクションに変換

fileList: Object[]型
java.util.Arrays.asList(fileList);

ファイルを読み込む

BufferedReader reader = null;
try {
	reader = new BufferedReader(new FileReader(path));
	//もしくは
	//new BufferedReader(
	//  new InputStreamReader(url.openStream(), "JISAutoDetect"));
  
	String line;
	while ((line = reader.readLine()) != null) {
		buffer.append(line);
		buffer.append("\n");
	}
} catch (FileNotFoundException e) {
	e.printStackTrace();
} catch (IOException e) {
	e.printStackTrace();
} finally {
	if (reader != null) {
		try {
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		reader = null;
	}
}

Objectのシリアライズ

try {
	//FileInputStreamオブジェクトの生成
	FileInputStream inFile =
		new FileInputStream([serializeしたファイル名]);

	//ObjectInputStreamオブジェクトの生成 
	ObjectInputStream inObject = new ObjectInputStream(inFile);

	Object object = (クラス名) inObject.readObject();

	inObject.close(); //オブジェクト入力ストリームのクローズ
	inFile.close(); //ファイル入力ストリームのクローズ

	//FileOutputStreamオブジェクトの生成
	FileOutputStream outFile =
		new FileOutputStream([書き出したいファイル名]);

	//ObjectOutputStreamオブジェクトの生成 
	ObjectOutputStream outObject = new ObjectOutputStream(outFile);

	//クラスVectorのオブジェクトの書き込み
	outObject.writeObject(object);

	outObject.close(); //オブジェクト出力ストリームのクローズ
	outFile.close(); //ファイル出力ストリームのクローズ

} catch (FileNotFoundException e) {
} catch (IOException e) {
} catch (ClassNotFoundException e) {
}

オブジェクトが配列かどうかをチェックする

String model = "hogehoge";
// String[] model = new String[] { "hogehoge", "hugahuga" };

List list = new ArrayList();
if (model.getClass().isArray()) {
    Object tmp = model;
    list = Arrays.asList((Object[]) tmp);
} else {
    list.add(model);
}
System.out.println(list);

インナクラス、無名クラス

インナークラス・無名クラスは、フィールドにはアクセス可能、ローカル変数には不可能。

finalなフィールドの初期化

宣言時に初期化する必要があるのかと思ってたんですが、コンストラクタ内でもよいんですね。知らなかった。


この記事は

選択肢 投票
おもしろかった 61  
そうでもない 11  
  • 役に立ちました。 プレースフォルダ→プレースホルダ(place holder)? -- 2005-04-25 12:56:23 (月)
  • お恥ずかしい。なおしました。ありがとうございました。 -- きの? 2005-04-25 16:39:01 (月)
  • ファイル アップロード -- 2010-07-06 (火) 14:14:42
  • いつも参考にしてます。 -- 通りすがりのSE? 2011-09-01 (木) 22:34:41
  • ハッシュ化のコードですが、md.digest()の後に、md.rest()呼んだ方がいいですね。 -- 通りすがりのSE? 2011-09-01 (木) 22:35:47

Top / Java / TIPS集

現在のアクセス:122061


*1 http://ja.wikipedia.org/wiki/Base64
*2 なぜ戻り値がbyte[]なのかは不明
*3 http://ja.wikipedia.org/wiki/SHA
*4 JSP1.1の場合は、pageEncoding が使えないので、ページの文字コードと出力文字コードは同じになります。
*5 URLクラスをそのままtoStringすると、よけいな文字(file)がついちゃうんですよね。
*6 スペースなどが入っている場合のために、URLDecoder#decodeをかけねばっ

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-07-21 (金) 19:13:02 (4d)