Problem z ClassLoaderem

ash

Problemy z klasami

Jedną z bardziej znienawidzonych przeze mnie rzeczy w języku Java jest ClassLoader. Niestety rządzi się on swoimi własnymi prawami i wszelkie modyfikacje jego ustawień są niemal niemożliwe. Poniżej prezentuję listę problemów, które najbardziej utrudniają mi życie wraz z rozwiązaniami (o ile są możliwe i o ile je znalazłem ;) ).

Problem 1. ładowanie nowych wersji klas

Problem wytłumaczę na przykladzie. Mamy jakiś modułowy system, w którym moduł główny ładuje i uruchamia inne moduły. W czasie rozwoju oprogramowawnia zmieniamy kod tylko w jednym z modułów pobocznych. Problem ujawnia się w ten sposób, że jeśli VM Javy załaduje raz klasę o danej nazwie, to przy powtórnym tworzeniu egzemplarza tej klasy brany jest kod poprzednio wczytany przez VM. Zatem nawet jeśli nowy kod jest w znanej ścieżce VM, nie zostanie on uwzględniony.

Rozwiązenie:
Brak! Jedyne co można zrobić, to uruchomić wszystko od początku. Może kiedyś przy czyszczniu pamięci VM skasuje załadowany kod, ale mi nie udało sie tego doczekać ;)

Problem 2. ścieżka dostępu

Problem również związany z ładowaniem klas. Wszysko jest proste, jeśli wiadomo gdzie znajdują się wszystkie potrzebne klasy. Wtedy wystarczy podać parametr classpath przy uruchamianiu VM i wszystko gra. Gorzej, gdy potrzeba dodać informację o ścieżce do klas dynamicznie w trakcie działania programu. Formalnie jest to niemożliwe, ale....

Rozwiązanie:
Poniżej prezentuję uproszczoną wersję klasy, która pozwala w sposób niezbyt "legalny" poradzić sobie z tym problemem. Jest to nieco zmodyfikowany i uproszczony kod znaleziony gdzieś na jakimś forum dyskusyjnym (tak dawno, że nie pamiętam gdzie).

import java.io.*;
import java.net.*;
import java.lang.reflect.*;

public class SetPath
{
  private static final Class[] parameters = new Class[]{URL.class};

  public static void addFile(String s) throws IOException
  {
    File f = new File(s);
    addFile(f);
  }

  public static void addFile(File f)
  {
    try {
      addURL(f.toURL());
    } catch ( MalformedURLException e ) {}
  }

  static void addURL(URL u)
  {
    URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
    Class sysclass = URLClassLoader.class;
    try {
      Method method = sysclass.getDeclaredMethod("addURL",parameters);
      method.setAccessible(true);
      method.invoke(sysloader,new Object[]{ u });
      } catch (Throwable t) {
        t.printStackTrace();
      }
  }
}

W miarę odkrywania i pokonywania problemów oraz w miarę wolnego czasu będę tutaj dopisywał moje nowe odkrycia. O ile w ogóle będzie zainteresowanie ;)

ash

2 komentarzy

Przed jakimikolwiek zabawami z dynamicznym ładowaniem klas warto wywołać coś takiego:
Compiler.disable();
Powoduje to dezaktywację JIT, a tym samym rozwiązanie problemu z rzekomym bugiem dynamicznego ładowania klas. Aktywny kompilator zawsze zmusza JVM do używania raz wstępnie skompilowanego kodu. A to uniemożliwia w praktyce dynamiczne ładowanie lub podmianę klas. Na całą resztę kodu nie związanego z ładowaniem klas należy z powrotem przywrócić JIT przez Compiler.enable();.

Bardzo przydatny dla mnie artykuł.