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 ;)
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ł.