вторник, 24 июля 2012 г.

Загрузка JAR из другого JAR. Часть 1

Приветствую!
Сегодня хочется поговорить о вопросе загрузки jar, находящегося внутри другого jar. При этом, мы предполагаем, что этот "большой" jar-файл был статически прилинкован к приложению и уже загружен.
К сожалению совсем легко это не сделать, но и усилий надо приложить совсем немного.
Единственный возможный выход - это сохранить эти файлы на файловой системе (лучше всего во временной папке) и передать URLClassLoader-у пути к файлам.

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

Итак, приступим. Наша задача достаточно проста:

  1. Находим внутренний jar через вызов getResource текущего ClassLoader
  2. Получаем у него InputStream
  3. Создаем временный файл
  4. Заливаем наш jar-ник во временный файл
  5. Закрываем все потоки
  6. URL временного файла скармливаем URLClassLoader
  7. Здесь на выбор - либо через ClassLoader вызываем loadClass, либо через Class.forName - загружаем нужный класс
  8. Повторяем все шаги для остальных jar-ков
А вот и код
private URL getInsideJarURL(ClassLoader cl, String resourceName) throws IOException {
        debug("Get inside JAR URL for resource {}", resourceName);
        try {
            URL resourceUrl = cl.getResource(resourceName);
            InputStream is = resourceUrl.openStream();
            File tempFile = File.createTempFile(resourceName, ".jar");
            tempFile.deleteOnExit();
            FileOutputStream fos = new FileOutputStream(tempFile);

            byte[] buf = new byte[1024];
            int len;
            while (0 < (len = is.read(buf, 0, 1024))) {
                fos.write(buf, 0, len);
            }

            is.close();
            fos.close();
            info("Saved inside JAR resource {} as {}", resourceName, tempFile.getAbsolutePath());
            return tempFile.toURI().toURL();
        } catch (IOException e) {
            error("Error loading resource jar {}", resourceName);
            throw e;
        }
    }
и его использование:
ClassLoader cl = getClass().getClassLoader();
classLoader = new URLClassLoader(new URL[] { getInsideJarURL(cl, "jar-1.jar"),
                    getInsideJarURL(cl, "jar-2.jar"), getInsideJarURL(cl, "jar-3.jar") }, cl);
Class<?> myClass = Class.forName("MyClass", true, classLoader);

Если есть желание подгрузить сразу все классы - это тоже не проблема.
Мы знаем наш файл, можем создать JarFile и пробежаться по его JarEntry.

Если мы хотим "скормить" URLClassLoader не всю нашу библиотеку, а лишь отдельные классы, то здесь тоже нет проблемы. Мы выгружаем библиотеку как и раньше, а вот дальше мы создаем объект JarFile на основе нашего файла и достаем те JarEntry, которые нам нужны и тоже сохраняем их в файлы. А затем отдаем URLClassLoader ссылки на выгруженные файлы классов.

Комментариев нет:

Отправить комментарий