2016-05-25 5 views
0

Мы пытаемся отладить невоспроизводимую проблему с помощью WebStart, где доступ к ресурсам внутри Jars «случайно» завершится неудачно. Возможно, каждый раз, когда запускается 1000 приложений, эта ошибка заканчивается, что может произойти в любом месте, где ресурсы считываются из банки.Перехват вызовов ClassLoader.getResource (String) с помощью специального класса ClassLoader

Поиск в базе данных Google и базы данных Java не принес ничего подобного (или, по крайней мере, ничего полезного).

Мы пытаемся получить дополнительную информацию о том, что происходит на клиенте, «применяя» приложение, чтобы мы отслеживали все звонки на ClassLoader.getResource(String) (включая косвенно более ClassLoader.getResourceAsStream(String)). Без изменения кода приложения я создал «пусковую установку», которая запускала бы все приложение с помощью специального загрузчика классов.

К сожалению, кажется, что мой ClassLoader каким-то образом обошел стороной. Я не вижу ожидаемого выхода System.out. Вот то, что я пробовал:

private static final class MyClassLoader extends ClassLoader { 
    private MyClassLoader() { 
     super(TheClassThatMainIsIn.class.getClassLoader()); 
    } 

    @Override 
    public URL getResource(String name) { 
     System.out.println("getResource("+name+")"); 
     // Snip 
     return super.getResource(name); 
    } 

    @Override 
    public InputStream getResourceAsStream(String name) { 
     System.out.println("getResourceAsStream("+name+")"); 
     final URL url = getResource(name); 
     try { 
      return url != null ? url.openStream() : null; 
     } catch (final IOException e) { 
      return null; 
     } 
    } 
} 

public static void main(String[] args) { 
    System.out.println("Starting MyRealApp Launcher ..."); 
    final MyClassLoader loader = new MyClassLoader(); 
    try { 
     Class<?> realAppClasss = loader.loadClass("MyRealAppClass"); 
     Method main = realAppClasss.getMethod("main", String[].class); 
     main.invoke(null, (Object) args); 
    } catch (final RuntimeException e) { 
     throw e; 
    } catch (final Error e) { 
     throw e; 
    } catch (final InvocationTargetException e) { 
     final Throwable cause = e.getCause(); 
     if (cause instanceof RuntimeException) { 
      throw (RuntimeException) cause; 
     } 
     if (cause instanceof Error) { 
      throw (Error) cause; 
     } 
     throw new UndeclaredThrowableException(cause); 
    } catch (final Throwable t) { 
     throw new UndeclaredThrowableException(t); 
    } 
} 

Что я здесь делаю неправильно?

+0

Вы переопределяете findClass() и loadClassData? – Sanjeev

+0

Нет, я не отменяю их; Меня не интересуют «классы»; просто «ресурсы». Нужно ли мне? Если да, то что мне делать дальше, чем просто передать вызов базовому классу? –

+0

Нет, вам не нужно. Я не понял ваш вопрос в первый раз, когда я его прочитал. То, что вы делаете, кажется правильным. Я бы предложил включить исходный код ClassLoader в ваш проект, пройдя через это с помощью отладчика, чтобы понять, почему он не вызывает ваш переопределенный метод. – Sanjeev

ответ

2

Да. Это работает, в принципе.

Однако вы должны учесть, как код загрузки ресурса получает в загрузчик классов. Поскольку класс не отображается, похоже, что они используют загрузчик классов родителей. Вы имеете учитывать различные сценарии:

код с использованием контекста загрузчика класса, как:

Thread.currentThread().getContextClassLoader().getResource("via-context"); 

Это легко достичь, установив его перед вызовом в основной:

Thread.currentThread().setContextClassLoader(loader); 
Method main = realAppClasss.getMethod("main", String[].class); 
main.invoke(null, (Object) args); 

Далее вещь, которую вы должны учитывать, - это код, который «берет» загрузчик классов из текущего класса и загружает его. Когда вы загружаете класс через загрузчик родительского класса, он также будет использовать этот загрузчик классов для получения ресурса. Как:

MyRealAppClass.class.getResource("via-class"); 
MyRealAppClass.class.getClassLoader().getResource("via-class"); 
objectInfApp.getClass().getClassLoader().getResource("via-class"); 

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

// URL class loader to lookup in jars etc 
private static class MyClassLoader extends URLClassLoader 
{ 
    public MyClassLoader(URL[] urls) { 
     // Use the given URLs and skip any parent class loader, directly go to the system loader 
     super(urls,null); 
    } 

// ... 

// Then setup the class path 
    String[] classPath = System.getProperty("java.class.path").split(";"); 
    URL[] classPathUrls = new URL[classPath.length]; 
    for (int i = 0; i < classPath.length; i++) { 
     classPathUrls[i] = new File(classPath[i]).toURL(); 

    } 
    MyClassLoader loader = new MyClassLoader(classPathUrls); 

Это должно охватывать самые основные случаи. Когда у вас есть фактическое приложение, у него больше обтекания загрузчика класса, вам может потребоваться больше настроек.

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