2015-12-11 2 views
3

Я пытаюсь динамически извлекать конфигурацию из данного файла WAR. Моя цель - найти все классы, которые реализуют некоторый интерфейс (Parameter).Построить класс классов из JAR в файле WAR

Файл войны не находится в пути к классам, поэтому я создаю временный загрузчик классов для проверки его классов.

URL webClasses = new URL("jar","","file:" + new File(path).getAbsolutePath() + "!/WEB-INF/classes/"); 
URLClassLoader cl = URLClassLoader.newInstance(new URL[] { webClasses }); 
Reflections reflections = new Reflections(ClasspathHelper.forClassLoader(cl), new SubTypesScanner(), cl); 
Set<Class<? extends Parameter>> params = reflections.getSubTypesOf(Parameter.class); 

Это прекрасно работает. Я нахожу всю реализацию Parameter в моем webapp.

Теперь, я хотел бы сделать то же самое для каждой баночки, присутствующей в WEB-INF/lib. К сожалению, я не нашел способ построить загрузчик классов в этом случае. Похоже, что загрузчик классов игнорирует jar URLs (jar:<url>!/WEB-INF/lib/{entry}.jar).

Когда я запускаю этот код, не найден класс Reflections:

Set<URL> libs = findLibJars(warUrl)); 
// The URLs are in the following format : {myWar}.war!/WEB-INF/lib/{myLib}.jar 
URLClassLoader cl = URLClassLoader.newInstance(urls.toArray(new URL[urls.size()])); 
cl.loadClass("my.company.config.AppParameter"); 

Если возможно, я хотел бы избежать необходимости извлекать WEB-INF/lib/*.jar из файла WAR и анализировать их по отдельности.

У меня что-то отсутствует? (другим способом сделать это, другим способом создания загрузчика классов, ... любой свинец поможет). Может быть, это можно сделать без использования библиотеки Reflections?

+0

Обычно веб-сервер, основанный на сервлете, распаковывает файл войны, а затем вы можете использовать их в какой-либо подпапке. Не будет ли проще работать с извлеченными файлами? –

+0

Я признаю, что это в основном любопытство, которое подталкивает меня к поиску такого решения. Извлечение файлов действительно способ, которым я знаю, будет работать, но поскольку цель состоит исключительно в том, чтобы осматривать войну и обнаруживать некоторые данные конфигурации. Я бы хотел, чтобы это действие было быстрым (например, путем фильтрации по пакету, поскольку только пакет, начинающийся с my.company, потенциально содержит классы, которые я ищу). В случае, если файл War содержит много сторонних зависимостей, было бы ненужным извлечение, а затем удалить их –

+0

, но разве он не был развернут? Тогда файлы должны быть там. «Война» - это просто zip-файл. –

ответ

2

Это, безусловно, возможно. Вот уродливый пример делает это:

String warName = "wlaj.war"; 
ClassLoader loader = new ClassLoader() { 
    @Override 
    protected Class<?> findClass(String name) throws ClassNotFoundException { 
     // This probably needs fixing: 
     String fileName = name.replace('.', '/') + ".class"; 
     try { 
      try (ZipFile zf = new ZipFile(warName)) { 
       ZipEntry jar = zf.getEntry("WEB-INF/lib/jlaj.jar"); 
       if (jar == null) 
        throw new ClassNotFoundException("No jlaj.jar"); 
       try (ZipInputStream jarInput = new ZipInputStream(zf.getInputStream(jar))) { 
        for (ZipEntry cl; (cl = jarInput.getNextEntry()) != null;) { 
         if (fileName.equals(cl.getName())) { 
          ByteArrayOutputStream data = new ByteArrayOutputStream(); 
          byte[] buffer = new byte[4096]; 
          for (int len; (len = jarInput.read(buffer)) != -1;) { 
           data.write(buffer, 0, len); 
          } 
          buffer = data.toByteArray(); 
          return defineClass(name, buffer, 0, buffer.length); 
         } 
        } 
       } 
      } 
      throw new ClassNotFoundException(); 
     } catch (IOException ex) { 
      throw new ClassNotFoundException("Error opening class file", ex); 
     } 
    } 
}; 
loader.loadClass("jlaj.NewJFrame"); 

Проблемы:

  1. нужен элегантный безопасный способ, чтобы преобразовать имя класса в путь к файлу внутри JAR. В моем случае "jlaj.NewJFrame" -> "jlaj/NewJFrame.class", но он становится очень уродливым при попытке загрузить внутренние классы и, возможно, в некоторых других ситуациях, о которых я даже не могу думать.
  2. Чтение данных класса должно, вероятно, принадлежать некоторой утилите readAll, потому что это выглядит очень грязно.
  3. Очевидно, для этого требуется не анонимный класс с именем WAR и внутренним именем JAR, переданным конструктору. Или ZipFile и ZipEntry, если вы хотите итерации над ними извне.

С положительной стороны он работает.

+0

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

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