実行中クラスのクラスパスもしくはjarファイルの取得

2018年12月18日

Javaのプログラム実行時に、以下を検出したい場合がある。

  • Eclipse等の開発環境で動作しているのか、つまり一時的にコンパイル出力された.classの集合の上で動作しているのか、あるいは製品としてjarにまとめられた状態で動作しているのか。
  • 開発環境であれば、そのクラスパスは何か。
  • 製品jarであれば、そのjarファイルは何か。

これを検出する。

package sample;

import java.io.*;
import java.net.*;

/**
 * 実行中のクラスのクラスパスを取得する。
 * 開発環境の場合は、そのクラスパス(フォルダ)、jarでの実行の場合はjarファイルを返す。
 * @author ysugimura
 */
public class ClassPathLocator {

  /**
   * 実行中のjarファイルを返す。jarでない場合(開発環境の場合)はクラスパスを返す。
   * @return jarファイル、あるいはクラスパス
   */
  public static File getLocation() {    
    // このクラスのリソースURLを求める
    URL classUrl = ClassPathLocator.class.getResource(ClassPathLocator.class.getSimpleName()  + ".class");

    // ドットの数を数えて、このクラスのパッケージ階層数を取得する。
    int pkgHier =  (int)ClassPathLocator.class.getName().chars().filter(c->c == '.').count();

    // このクラスの格納されているjarファイルを取得する。jarでない場合(開発環境の場合)はフォルダを返す。
    return getLocationFromClassUrl(classUrl, pkgHier);
  }

  /** 
   * .classのリソースURLからjarファイルを取得する。
   * jarでの実行でない場合(開発環境の場合)はクラスパスを返す。
   * @param url .classのリソースURL
   * @param pkgHier このクラスのパッケージ階層数
   * @return .classがjar内にある場合、そのjarファイル。jarではない場合はそのクラスパス
   */
  static File getLocationFromClassUrl(URL url, int pkgHier) {   
    // jar:の場合
    if (url.getProtocol().equals("jar")) {
      try {
        JarURLConnection conn = (JarURLConnection)url.openConnection();
//修正した return new File(conn.getJarFileURL().getFile());
        return URLUtil.getFile(conn.getJarFileURL());
      } catch (Exception ex) {
        return null;
      }
    } 

    // file:の場合
    if (url.getProtocol().equals("file")) {
      File file = new File(url.getFile()).getParentFile();
      for (int i = 0; i < pkgHier; i++) file = file.getParentFile();      
      return file;
    }

    // ありえない
    throw new RuntimeException("Could not determine jar or file");
  }
}

URLUtil

/**
 * {@link URL#getFile()}は使用できない。
 * URLでは漢字等がエンコーディングされているが、getFile()はエンコーディング状態のまま取得してしまう。
 * @author ysugimura
 */
public class URLUtil {

  /** fileの{@link URL}から{@link Path}を取得する */
  public static Path getPath(URL url) {
    try {
      return Paths.get(url.toURI());    
    } catch (URISyntaxException ex) {
      throw new RuntimeException(ex);
    }
  }

  /** fileの{@link URL}から{@link File}を取得する */
  public static File getFile(URL url)  {
    return getPath(url).toFile();
  }  
}

ClassPathLocator.getLocation()で返されるFileは、

  • 開発環境であれば、そのクラスパスであるフォルダ
  • 製品jarであれば、そのjarファイル

になる。File#isDirectory()を使って両者を区別する。