2015-10-30 1 views
2

У меня очень странная проблема. Я сделал некоторые очень сумасшедшие вещи: я превратил жир uber-jar библиотек hadoop, которые я собрал с плагином sbt-assembly для DLL с использованием IKVM. Я написал небольшую тестовую программу, которая просто сводится к следующему:.net: загрузка зависимостей для библиотек DLL, отличных от EXE?

var u = new java.net.URI("hdfs://my-namenode:8020/"); 
var fs = org.apache.hadoop.fs.FileSystem.get(u, new org.apache.hadoop.conf.Configuration()); 
foreach(var s in fs.listStatus(new org.apache.hadoop.fs.Path("/"))) { 
    Console.WriteLine(s.getPath().toString()); 
} 

Когда я запускаю это в консольном приложении с моей hadoop.dll и необходимыми библиотеками DLL IKVM добавлен как ссылки, это перечисляет содержание моего HDFS ,

Однако, когда я оберните именно этот код в DLL, добавьте ту же зависимость в эту DLL и вызвать это от моего консольного приложения, я получаю:

No FileSystem for scheme: hdfs 

Когда я указать правильное имя класса в моем Hadoop conf через ключ fs.hdfs.impl, я получаю ClassNotFoundException.

Являются ли зависимости в разных исполняемых файлах по-разному, чем в DLL, или это может быть специфическое поведение IKVM?

EDIT: Другое странное поведение: когда я конструирую FileSystem один раз в своем консольном приложении, а THEN вызывает этот метод в DLL, он запускается.

ответ

1

Я нашел ответ сам (опять-таки ...)

Он не должен делать, как .net обрабатывает загрузку зависимостей, но как IKVM (и с этим уважение Java) обрабатывает динамическую загрузку классов ,

я вырыл вокруг исходного кода Hadoop, и нашел следующий фрагмент:

private ClassLoader classLoader; 
{ 
    classLoader = Thread.currentThread().getContextClassLoader(); 
    if (classLoader == null) { 
    classLoader = Configuration.class.getClassLoader(); 
    } 
} 

Линия classLoader = Thread.currentThread().getContextClassLoader(); представляет особый интерес. Погрузчик контекстного класса моего консольного приложения - это его контекст - без ссылки на какой-либо из классов Hadoop, поэтому ClassNotFoundException при явной установке fs.hdfs.impl на org.apache.hadoop.hdfs.DistributedFileSystem.

К счастью, Configuration класс имеет метод setClassLoader, поэтому при этом при построении конфигурации:

var conf = new org.apache.hadoop.conf.Configuration(); 
conf.setClassLoader(conf.getClass().getClassLoader()); 
conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem"); 

это работает! Это связано с тем, что возвращает загрузчик классов conf контекст - т. Е. Преобразованный uber-jar hadoop.dll, который имеет класс.

Это по-прежнему необходимо явно указать классы файловой системы с fs.XXXX.impl хотя, потому что автоматическая файловая механизм разрешения выглядит следующим образом:

private static void loadFileSystems() { 
    synchronized (FileSystem.class) { 
    if (!FILE_SYSTEMS_LOADED) { 
     ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class); 
     for (FileSystem fs : serviceLoader) { 
     SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass()); 
     } 
     FILE_SYSTEMS_LOADED = true; 
    } 
    } 

Как вы можете видеть, файловые системы будут решены здесь:

ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class); 

этот метод снова использует Thread.currentThread().getContextClassLoader(), что означает мое консольное приложение, которое не имеет классов hadoop.

Итак, tl; dr: после создания Configuration, установите его ClassLoader вручную для загрузчика класса контекста dll.

 Смежные вопросы

  • Нет связанных вопросов^_^