ctree:クラスパス中のクラス一覧、衝突検出、ツリー構造化

2018年6月7日



※弊社製オープンソースソフトはオープンソースソフトウェアとしてまとめているので参照されたい。

このライブラリは、かなり特殊な用途である。行うことは、

  • 実行中のjarファイルなりフォルダ(Eclipse開発環境等の場合)の中にあるクラスの一覧を取得する。
  • もしくは、実行中の全クラスパスの中の全クラスの一覧を取得する。
  • 二番目の方法で取得した場合には、クラスの衝突が無いかをチェックする。
  • クラス一覧ををツリー構造に構成する。

このライブラリの用途としては、もちろんクラスの衝突を検出することである。ありとあらゆるライブラリjarを利用すると、その中には衝突してしまっているものもある。これを検出することである。

もう一つは、プログラムの実行中にLog4jの設定を変更してログ対象クラスを変更したかったことだ。基本的にLog4jの設定は、最も簡単なcommons-logging + Log4jの使い方にも書いたことだが、プログラム実行中に変更することができる。

わざわざ、実行を停止し、プロパティファイルを書き直し、再実行するなどという必要はない。

この目的のため、単にクラス名をクリックすれば、そのクラス名をLog4jのプロパティファイルに書き込むというシステムになっているのだが、もちろんその分のコードはctreeには含まれない。基本的にはあくまでクラス一覧を取得することである。

githubはhttps://github.com/ysugimura/ctreeになる。
Mavenリポジトリはhttps://ysugimura.github.io/mavenにある。

以下は、githubにあるサンプルである。

Sample1

以下はSample1が含まれるフォルダあるいはjarファイルにあるクラス一覧を表示する。

public class Sample1 {
  public static void main(String[]args) throws IOException {
    ClassNames classNames = ClassNames.loadFor(Sample1.class);
    classNames.getNameSet().forEach(System.out::println);
  }
}

実行結果は以下である。

com.cm55.ctree.ClassNames
com.cm55.ctree.sample.Sample3
com.cm55.ctree.sample.Sample2
com.cm55.ctree.sample.TreeAdapterImpl
com.cm55.ctree.ClassPathLocator
com.cm55.ctree.sample.JavaClassTree$PackageNode
com.cm55.ctree.sample.JavaClassTree$ClassNode
....

Sample2

以下はSample2が含まれるクラスローダ内にあるすべてのクラスの一覧を表示し、さらに衝突をチェックする。

※注意事項:これはJava9では動作しない。Java9のシステムクラスローダがURLClassLoaderでは無いからだ。Java8でのみ動作する。Java9以降での対処方法は現在のところ不明。

public class Sample2 {
  public static void main(String[]args) throws IOException {
    List<ClassNames>classNamesList = ClassNames.loadAllFor(Sample2.class);
    classNamesList.forEach(classNames-> {
      System.out.println(classNames.getFile());
      classNames.getNameSet().forEach(n-> System.out.println("  " + n));      
    });     
    System.out.println("conflicts....");
    System.out.println(ClassNamesBinder.checkConflict(classNamesList));
  }
}

クラスローダにあるすべてのJar及びフォルダなので、当然のことながら、Javaそれ自体のライブラリも含み、出力は膨大なものになる。また、この例では衝突が無いのだが、あればconflicts….の後に出力されるはず。

C:\Program Files\Java\jdk1.8.0_111\jre\lib\resources.jar
C:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar
  com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data
  com.sun.org.apache.xml.internal.security.transforms.implementations.TransformC14N11
  com.sun.xml.internal.ws.encoding.fastinfoset.FastInfosetStreamSOAPCodec
  sun.text.normalizer.NormalizerImpl$NormTrieImpl
  com.sun.jmx.remote.internal.NotificationBuffer
  sun.tools.jar.resources.jar_sv
  com.sun.java.swing.plaf.motif.resources.motif
  com.sun.xml.internal.stream.dtd.nonvalidating.XMLNotationDecl
  javax.print.attribute.standard.OutputDeviceAssigned
  sun.awt.AWTAccessor$MenuAccessor
  sun.rmi.server.ActivatableRef
..........
  com.cm55.ctree.sample.package-info
  com.cm55.ctree.ClassNamesBinderTest
  com.cm55.ctree.package-info
conflicts....

Sample3

今度は、このJarファイルあるいはフォルダ(Eclipse開発環境等の場合)の全クラスについて、それを木構造化してみる。

これには、木構造を表すノードと、ノード作成用のアダプタ必要になるが、それらはここでは示さない。

public class Sample3 {
  public static void main(String[]args) throws IOException {
    ClassNames classNames = ClassNames.loadFor(Sample1.class);
    PackageNode root = TreeCreator.createTree(classNames, new TreeAdapterImpl());    
    System.out.println(JavaClassTree.toString(root));
  }
}

木構造に従ってインデントした結果の出力は以下のようになる。

com
 cm55
  ctree
   ClassNames
   sample
    Sample3
    Sample2
    TreeAdapterImpl
    JavaClassTree$PackageNode
    JavaClassTree$ClassNode
    Sample1
    JavaClassTree$Node
    JavaClassTree
    package-info
   ClassPathLocator
   ClassNamesBinder
   TreeAdapter
   TreeCreator
   ClassNamesBinderTest
   package-info
  package-info