13
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Javaプロジェクト内でバージョン違いの重複jarを探し出す方法

Last updated at Posted at 2014-12-12

mavenなどでいろいろなフレームワークを試してみるときに
依存するプロジェクトがそれぞれ違うバージョンのlog4jなどに依存している場合があります。
単一バージョンのlog4jのみを使用するように、うまくpom.xmlファイルでexlcudeされていれば問題ないです。
しかしそうなっていない場合、コンパイルは通るものの、実行時に難解な例外やエラーが出てしまいます。
そのときに一度試してみて頂きたいのが下記の方法で、複数のjarに重複するパッケージ・クラスを持ったものがいないか探し出す方法です。
もっとよい方法があれば教えてください。お願いします。

###テスト環境

Java 17
eclipse 2022-06

###サンプル環境構成

project
testprj
│
├─libs
│  └─log4j
│      ├─1.2.12
│      │      log4j-1.2.12.jar
│      │
│      ├─1.2.14
│      │      log4j-1.2.14.jar
│      │
│      ├─1.2.16
│      │      log4j-1.2.16.jar
│      │
│      └─1.2.17
│              log4j-1.2.17.jar
│
└─src
    └─jp
        └─co
            └─panda
                └─testprj
                        Main.java

バージョン違いのLog4jのjarをlib以下に配置し、クラスパスに追加します。

###サンプルコード

実行するOSによりクラスパスの区切り文字を変更してください。
エラー処理などは手を抜いています。あしからず。

Main.java

package jp.co.panda.testprj;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

	private static final String CLASS_PATH_SEPARATOR = ";";//windows

	public static void main(String[] args) {
		// クラスパスを取得する
		final String[] classPathArray = System.getProperties()
				.getProperty("java.class.path", null)
				.split(CLASS_PATH_SEPARATOR);

		// jar名称とjarに含まれるクラスを調べてリストにする
		List<ClassPathData> classpathDataList = Stream.of(classPathArray)
			.filter(classpath -> classpath.endsWith(".jar"))
			.map(classpath -> {
				try (JarFile jarFile = new JarFile(new File(classpath))) {
					// jar名を取得する
					String[] sp = classpath.split("\\\\");
					final String jarName = sp[sp.length - 1];

					// jar内に含まれているクラス情報を取得する
					// META-INFとかは除外している
					List<ClassPathData> dataList = Collections.list(jarFile.entries()).stream()
							.map(jarEntry -> jarEntry.toString())
							.filter(jarEntry -> jarEntry.endsWith(".class"))
							.map(jarEntry -> {
								return new ClassPathData(
										jarName,
										jarEntry
								);
							})
							.collect(Collectors.toList());
					
					return dataList.stream();
				} catch (IOException e) {
					throw new RuntimeException();
				}
			})
			.flatMap(stream -> stream)
			.collect(Collectors.toList());

		// フルクラス名でグルーピングし、2個以上重複しているものを出力する
		classpathDataList.stream()
			.collect(Collectors.groupingBy(ClassPathData::getClasspath))
			.entrySet()
			.stream()
			.filter(entry -> entry.getValue().size() > 1)
			.sorted((a, b) -> a.getValue().size() - b.getValue().size())
			.sorted((a, b) -> a.getKey().compareTo(b.getKey()))
			.forEach(entry -> {
				System.out.println(entry.getKey());
				entry.getValue().stream()
					.map(data -> data.getJarname())
					.map(str -> "\t" + str)
					.sorted()
					.forEach(System.out::println);
			});
	}

	/**
	 * jar名称とjarに含まれるクラスを保持する
	 */
	static class ClassPathData {
		private String jarname;
		private String classpath;
		
		public ClassPathData(String jarname, String classpath) {
			this.jarname = jarname;
			this.classpath = classpath;
		}
		
		public String getJarname() {
			return jarname;
		}
		public void setJarname(String jarname) {
			this.jarname = jarname;
		}
		public String getClasspath() {
			return classpath;
		}
		public void setClasspath(String classpath) {
			this.classpath = classpath;
		}
	}

}

このコードを実行することで、出力されたログにはクラスが重複しているjar名をクラス単位に表記します。

org/apache/log4j/Appender.class
	log4j-1.2.12.jar
	log4j-1.2.14.jar
	log4j-1.2.16.jar
	log4j-1.2.17.jar
org/apache/log4j/AppenderSkeleton.class
	log4j-1.2.12.jar
	log4j-1.2.14.jar
	log4j-1.2.16.jar
	log4j-1.2.17.jar
org/apache/log4j/AsyncAppender$DiscardSummary.class
	log4j-1.2.14.jar
	log4j-1.2.16.jar
	log4j-1.2.17.jar
org/apache/log4j/AsyncAppender$Dispatcher.class
	log4j-1.2.14.jar
	log4j-1.2.16.jar
	log4j-1.2.17.jar
 
...以降省略

13
14
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?