Jak mozna uniknąć NoClassDefFoundError?

26 views
Skip to first unread message

Ula K.

unread,
Feb 23, 2011, 6:17:09 AM2/23/11
to jug-p...@googlegroups.com
Hej,

Mam następujący problem:
- korzystam z biblioteki Javovej
- wewnętrznie ta biblioteka wykonuje transformacje XSLT, w zależności od konfiguracji korzysta z JAXP lub biblioteki Saxon
- stworzyłam taką konfigurację, żeby biblioteka korzystała z JAXP
- mimo to przy wykonywaniu operacji dostaję NoClassDefFoundError: net/sf/saxon/TransformerFactoryImpl

Kod, który potrzebuje Saxona się nigdy nie wykonuje. Czy mogę jakoś uniknąć tego błędu? Powiem od razu, że wolałabym nie dodawać saxon.jar do projektu, kłóci się on z używaną już biblioteką do parsowania XMLa.

Pozdrawiam

--
Urszula Krukar
ukr...@gmail.com

Dawid Weiss

unread,
Feb 23, 2011, 6:29:25 AM2/23/11
to jug-p...@googlegroups.com
Podaj pełen stack trace, Ula, to będzie widać skąd ten wyjątek się
bierze. Rozwiązań jest parę -- od poszukania czy nie masz w
META-INF/services gdzieś przesłoniętego parsera (w którymś z jarów w
classpath) do owinięcia problematycznej biblioteki AspectJem w
runtime...

Dawid

2011/2/23 Ula K. <ukr...@gmail.com>:

> --
> Otrzymujesz tę wiadomość, ponieważ subskrybujesz grupę dyskusyjną Google o
> nazwie „Poznań Java User Group”.
> Aby zamieszczać posty w tej grupie, wyślij e-mail na adres
> jug-p...@googlegroups.com.
> Aby anulować subskrypcję tej grupy, wyślij e-mail na adres
> jug-poznan+...@googlegroups.com.
> Aby uzyskać więcej informacji, odwiedź tę grupę pod adresem
> http://groups.google.com/group/jug-poznan?hl=pl.
>

Leszek Gawron

unread,
Feb 23, 2011, 6:36:03 AM2/23/11
to jug-p...@googlegroups.com
On 2011-02-23 12:29, Dawid Weiss wrote:
> Podaj pe�en stack trace, Ula, to b�dzie wida� sk�d ten wyj�tek si�
> bierze. Rozwi�za� jest par� -- od poszukania czy nie masz w
> META-INF/services gdzie� przes�oni�tego parsera (w kt�rym� z jar�w w
> classpath) do owini�cia problematycznej biblioteki AspectJem w
> runtime...
>

Dawid zawsze AspectJ wcisnie :)


skoro nie korzystasz z Saxona to mozna tez bezczelnie zmockowac
net.sf.saxon.TransformerFactoryImpl (albo zrobic proxy do innej fabryki)
- byle sie uruchomilo.

lg

--
Leszek Gawron http://www.mobilebox.pl/krs.html
CTO at MobileBox Ltd.

Dawid Weiss

unread,
Feb 23, 2011, 6:42:58 AM2/23/11
to jug-p...@googlegroups.com
Napisałem od...do. Moim zdaniem problem tkwi w tym, że parser jest
szukany przez service lookup i pewnie gdzieś w classpath jest owo
nieszczęsne wskazanie na niego. Naprawiłbym przyczynę, a nie łatał
skutek.

Dawid

2011/2/23 Leszek Gawron <lga...@mobilebox.pl>:


> On 2011-02-23 12:29, Dawid Weiss wrote:
>>

>> Podaj pełen stack trace, Ula, to będzie widać skąd ten wyjątek się
>> bierze. Rozwiązań jest parę -- od poszukania czy nie masz w
>> META-INF/services gdzieś przesłoniętego parsera (w którymś z jarów w

>> classpath) do owinięcia problematycznej biblioteki AspectJem w


>> runtime...
>>
>
> Dawid zawsze AspectJ wcisnie :)
>
>
> skoro nie korzystasz z Saxona to mozna tez bezczelnie zmockowac
> net.sf.saxon.TransformerFactoryImpl (albo zrobic proxy do innej fabryki) -
> byle sie uruchomilo.
>
>        lg
>
> --
> Leszek Gawron                         http://www.mobilebox.pl/krs.html
> CTO at MobileBox Ltd.
>

Ula K.

unread,
Feb 23, 2011, 6:47:20 AM2/23/11
to jug-p...@googlegroups.com
Oki. Po kolei.
Stack trace:
Caused by: java.lang.NoClassDefFoundError: net/sf/saxon/TransformerFactoryImpl
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at org.pentaho.di.core.plugins.PluginRegistry.loadClass(PluginRegistry.java:297)
PluginRegistry.loadClass ładuje inna klasę, która zależy od saxona.

W bibliotece saxon.jar jest plik META-INF/services/... które pewnie przesłania domyślnego parsera.

Ula
--
Urszula Krukar
ukr...@gmail.com

Maciej Biłas

unread,
Feb 23, 2011, 7:02:40 AM2/23/11
to jug-p...@googlegroups.com
Ula, czy w takim razie masz czy nie masz saxon.jar w classpathie
projektu, z którego podajesz nam teraz stacktrace?

Jeżeli masz to najprawdopodobniej saxon.jar zawiera plik
META-INF/services/javax.xml.transform.TransformerFactory, gdzie jest
odwołanie do fabryki Saxona, która faktycznie przesłania tą domyślną.
Ale wtedy nie powinnaś dostawać NoClassDefFoundError…

Sprawdź, czy inny z jarów nie zawiera wyżej wymienionego pliku w
swojej strukturze.

Jaka jest kolejność kroków procedury, która szuka odpowiedniej
implementacji fabryki TransformerFactory można przeczytać tutaj:
http://download.oracle.com/javase/6/docs/api/javax/xml/transform/TransformerFactory.html#newInstance()

Jeżeli chcesz być pewna jaka implementacja zostanie wykorzystana
najlepiej podaj ją ręcznie do drugiej wersji metody
newInstance(String, ClassLoader), np.
"javax.xml.transform.sax.SAXTransformerFactory".

-- Maciej

2011/2/23 Ula K. <ukr...@gmail.com>:


> Oki. Po kolei.
> Stack trace:
>
> Caused by: java.lang.NoClassDefFoundError:
> net/sf/saxon/TransformerFactoryImpl
> at java.lang.Class.forName0(Native Method)
> at java.lang.Class.forName(Class.java:169)
>
> at
> org.pentaho.di.core.plugins.PluginRegistry.loadClass(PluginRegistry.java:297)
>
> PluginRegistry.loadClass ładuje inna klasę, która zależy od saxona.
>
> W bibliotece saxon.jar jest plik META-INF/services/... które pewnie
> przesłania domyślnego parsera.
>
> Ula
>


--
Maciej Biłas
maciej at inszy dot org

Leszek Gawron

unread,
Feb 23, 2011, 7:05:55 AM2/23/11
to jug-p...@googlegroups.com
On 2011-02-23 12:47, Ula K. wrote:
> Oki. Po kolei.
> Stack trace:
>
> Caused by: java.lang.NoClassDefFoundError: net/sf/saxon/TransformerFactoryImpl
> at java.lang.Class.forName0(Native Method)
> at java.lang.Class.forName(Class.java:169)
>
> at org.pentaho.di.core.plugins.PluginRegistry.loadClass(PluginRegistry.java:297)
>
> PluginRegistry.loadClass �aduje inna klas�, kt�ra zale�y od saxona.
>
> W bibliotece saxon.jar jest plik META-INF/services/... kt�re pewnie
> przes�ania domy�lnego parsera.

a wiec dodalas saxon.jar do classpath, czy nie?

>
> Ula
>
> W dniu 23 lutego 2011 12:42 u�ytkownik Dawid Weiss
> <dawid...@cs.put.poznan.pl <mailto:dawid...@cs.put.poznan.pl>>
> napisaďż˝:
>
> Napisa�em od...do.

ale� �art przecie� :)

Ula K.

unread,
Feb 23, 2011, 7:07:31 AM2/23/11
to jug-p...@googlegroups.com
Hej,

Jeśli nie mam saxona w classpathie: dostaje NoClassDefFoundError.

Jeśli mam w classpathie: są problemy z systemem, bo nagle inna klasa jest używana do parsowania XMLa w innych miejscach.

Nie mam wpływu na to jak jest tworzony TransformerFactory, jest to robione wewnątrz zewnętrznej biblioteki.

Zaczynam się zastanawiać, czy po prostu nie usunąć META-INF/services z saxon.jar i tak to rozwiązać.

Ula

W dniu 23 lutego 2011 13:02 użytkownik Maciej Biłas <mac...@inszy.org> napisał:
Ula, czy w takim razie masz czy nie masz saxon.jar w classpathie
projektu, z którego podajesz nam teraz stacktrace?

Jeżeli masz to najprawdopodobniej saxon.jar zawiera plik
META-INF/services/javax.xml.transform.TransformerFactory, gdzie jest
odwołanie do fabryki Saxona, która faktycznie przesłania tą domyślną.
Ale wtedy nie powinnaś dostawać NoClassDefFoundError...

--
Otrzymujesz tę wiadomość, ponieważ subskrybujesz grupę dyskusyjną Google o nazwie "Poznań Java User Group".

Aby zamieszczać posty w tej grupie, wyślij e-mail na adres jug-p...@googlegroups.com.
Aby anulować subskrypcję tej grupy, wyślij e-mail na adres jug-poznan+...@googlegroups.com.
Aby uzyskać więcej informacji, odwiedź tę grupę pod adresem http://groups.google.com/group/jug-poznan?hl=pl.




--
Urszula Krukar
ukr...@gmail.com

Dawid Weiss

unread,
Feb 23, 2011, 7:47:35 AM2/23/11
to jug-p...@googlegroups.com
> Jeśli nie mam saxona w classpathie: dostaje NoClassDefFoundError.

Wygląda na to, że tajemnicza inna biblioteka używa go bezpośrednio.
Jak pytałem o stack, to pełen, a ta część, której nie podałaś to
właśnie istota problemu -- jadem zajrzyj do środka klasy, która robi
ClassNotFound i zobacz gdzie ona fizycznie używa tego Saxona i do
czego. Pewnie instancjonuje bezpośrednio klasę factory -- to ichni
błąd, trudno.

> Jeśli mam w classpathie: są problemy z systemem, bo nagle inna klasa jest
> używana do parsowania XMLa w innych miejscach.

Z przyczyn podanych przeze mnie i Macieja -- zmienia się domyślne factory.

> Nie mam wpływu na to jak jest tworzony TransformerFactory, jest to robione
> wewnątrz zewnętrznej biblioteki.

1. Możesz więc zrobić tak: hack na bibliotekę (statycznie zaaplikowany
aspekt na samą bibliotekę lub jej przekompilowanie jeśli masz źródła).
Rezultat: spójnie wszędzie będzie używany ten sam parser.

2. Zrobić adapter z API Saxona tak, jak napisał Leszek i tam
przekierować na domyślny parser systemowy. Rezultat: jak wyżej, tylko
w inny sposób.

3. Wywalić META-INF/services z saxon.jar. Rezultat: będziesz miała dwa
parsery pewnie w runtime, ale jeśli to nikogo nie boli, to nie ma
problemu.

4. Używać saxona globalnie. Rezultat: powinno być ok. Saxon jest dosyć
dobry i w miarę szybki.

D.

Tomek Matynia

unread,
Feb 23, 2011, 8:12:23 AM2/23/11
to jug-p...@googlegroups.com
NoClassDefFoundError dotyczy raczej innego problemu niż
ClassNotfounException. ClassNotFoundException jest sytuacją gdy klasy
rzeczywiście brakuje na classpath (albo dokładniej nie może być
wczytana z danego classloadera). Z drugiej strony NoClassDefFoundError
dotyczy problemu odczytania definicji - zazwyczaj klasa ładowana
(czyli dostępna na classpath) przez classloader wymaga "statycznie"
innej klasy. Wygląda na to, ze net/sf/saxon/TransformerFactoryImpl
jest na classpath, lecz odwołuje się do innej klasy, której
classloader nie może załadować i występuje błąd maszyny. Myślę, że
warto poszukać w tym kierunku (może saxon.jar jest dostarczony w
kontenerze, z którego korzystasz?).

Pozdrawiam,
Tomek

W dniu 23 lutego 2011 13:47 użytkownik Dawid Weiss
<dawid...@cs.put.poznan.pl> napisał:

Dawid Weiss

unread,
Feb 23, 2011, 8:22:33 AM2/23/11
to jug-p...@googlegroups.com
Ula dokładnie tak napisała, więc wszystko gra -- ta biblioteka ma link
statyczny do Saxona, a Ula nie chce Saxona :) Swoją drogą:

"Kod, który potrzebuje Saxona się nigdy nie wykonuje."

Jeśli jest tak, jak piszesz, to dość ciekawe. Nie pamiętam jak to
jest, ale class name resolution jest chyba odroczony jeśli to jest
wywołanie z kodu (to można łatwo sprawdzić, jeśli ktoś ma czas --
hint, hint :), więc ta klasa ma albo pole, albo statyczny inicjalizer,
albo hierarchię dziedziczenia związaną z saxonem. Nie możesz rozumiem
powiedzieć co to? Na priva nawet? :)

D.

2011/2/23 Tomek Matynia <tomek....@gmail.com>:

Ula K.

unread,
Feb 23, 2011, 8:32:26 AM2/23/11
to jug-p...@googlegroups.com
Moge powiedzieć.
Chodzi o project Pentaho Data Integration, aka Kettle, a konkretnie klasa org.pentaho.di.job.entries.xslt.JobEntryXSLT.

Sprawdziłam source code i jest bezczelne wołanie konstruktora:
transformer = new net.sf.saxon.TransformerFactoryImpl();

Klasa nie ma żadnego pola, hierarchii dziecziczenia itp. W skompilowanej klasie jest import do saxona i to najwidoczniej wystarczy, żeby JVM próbował ją załadować razem z ładowaniem klasy JobEntryXSLT.

Ostateczne rozwiązanie:
Dodałam pusty! plik META-INF/services/javax.xml.transform.TransformerFactory do katalogów src/main/webapp oraz src/test/resources i działa :)

Ula
Urszula Krukar
ukr...@gmail.com

Tomek Matynia

unread,
Feb 23, 2011, 8:40:22 AM2/23/11
to jug-p...@googlegroups.com
Zamysłem projektantów biblioteki i języka było chyba to, by sytuacjami
wyjątkowymi oznaczyć miejsca, gdzie programista chciałby skorzystać z
refleksji i w przypadku braku odpowiednich zależności dostał sytuację
wyjątkową (ClassNotFoundException), którą mógłby obsłużyć. W
przypadkach, gdy klasy zostały skompilowane z zależnościami, a
zależności nie zostały dostarczone podczas uruchomienia występuje błąd
maszyny wirtualnej.

> "Kod, który potrzebuje Saxona się nigdy nie wykonuje."
>

> (...) więc ta klasa ma albo pole, albo statyczny inicjalizer,


> albo hierarchię dziedziczenia związaną z saxonem.

Na to właśnie wygląda. Klasa może nie być instancjonowana, jednak
jeśli jest ładowana, to błąd właśnie tam może wystąpić.

Pozdrawiam,
Tomek

W dniu 23 lutego 2011 14:22 użytkownik Dawid Weiss

Maciej Biłas

unread,
Feb 23, 2011, 8:53:44 AM2/23/11
to jug-p...@googlegroups.com
2011/2/23 Ula K. <ukr...@gmail.com>:

> Moge powiedzieć.
> Chodzi o project Pentaho Data Integration, aka Kettle, a konkretnie klasa
> org.pentaho.di.job.entries.xslt.JobEntryXSLT.
>
> Sprawdziłam source code i jest bezczelne wołanie konstruktora:
> transformer = new net.sf.saxon.TransformerFactoryImpl();
No tak… i nic dziwnego skąd NoClassDefError.

Tylko czy przypadkiem w takim razie stacktrace, o którym pisałaś
wcześniej nie był z innego miejsca w kodzie? Nie widzę tutaj wywołania
metody Class.forName(String)?

PS. Standardowa implementacja TransformerFactory to
javax.xml.transform.sax.SAXTransformerFactory. Możesz śmiało ustawić w
pliku usługi tę implementację podając nazwę w/w. klasy w jego treści
(i tyle).

-- m.

Ula K.

unread,
Feb 23, 2011, 8:57:21 AM2/23/11
to jug-p...@googlegroups.com
Ten stack trace to było:
Class.forName("org.pentaho.di.job.entries.xslt.JobEntryXSLT")

a rzucało wyjątek z zupełnie inną klasą nieznalezioną.

Ula

W dniu 23 lutego 2011 14:53 użytkownik Maciej Biłas <mac...@inszy.org> napisał:
2011/2/23 Ula K. <ukr...@gmail.com>:
> Moge powiedzieć.
> Chodzi o project Pentaho Data Integration, aka Kettle, a konkretnie klasa
> org.pentaho.di.job.entries.xslt.JobEntryXSLT.
>
> Sprawdziłam source code i jest bezczelne wołanie konstruktora:
> transformer = new net.sf.saxon.TransformerFactoryImpl();
No tak... i nic dziwnego skąd NoClassDefError.


Tylko czy przypadkiem w takim razie stacktrace, o którym pisałaś
wcześniej nie był z innego miejsca w kodzie? Nie widzę tutaj wywołania
metody Class.forName(String)?

PS. Standardowa implementacja TransformerFactory to
javax.xml.transform.sax.SAXTransformerFactory. Możesz śmiało ustawić w
pliku usługi tę implementację podając nazwę w/w. klasy w jego treści
(i tyle).

-- m.

--
Maciej Biłas
maciej at inszy dot org

--
Otrzymujesz tę wiadomość, ponieważ subskrybujesz grupę dyskusyjną Google o nazwie "Poznań Java User Group".

Aby zamieszczać posty w tej grupie, wyślij e-mail na adres jug-p...@googlegroups.com.
Aby anulować subskrypcję tej grupy, wyślij e-mail na adres jug-poznan+...@googlegroups.com.
Aby uzyskać więcej informacji, odwiedź tę grupę pod adresem http://groups.google.com/group/jug-poznan?hl=pl.




--
Urszula Krukar
ukr...@gmail.com

Dawid Weiss

unread,
Feb 23, 2011, 9:26:28 AM2/23/11
to jug-p...@googlegroups.com
> Na to właśnie wygląda. Klasa może nie być instancjonowana, jednak
> jeśli jest ładowana, to błąd właśnie tam może wystąpić.

To nie jest prawda. import jest klauzulą kompilatora (języka), a nie
bytecode'u. Referencje do konkretnych klas znajdują się w constant
pool w klasie i w zasadzie nie są używane dopóki nie są wykonywane.
Możesz sprawdzić: uruchom sobie klasę z załącznika przez

java -cp test.jar test.A

Kod tej klasy odwołuje się do czegoś, czego nie ma, a wygląda tak:

package test;

public class A {
public static void main(String [] args) {
System.out.println("Hello.");
}

public void deref() {
new B();
}
}

Dopóki nie wywołasz deref (i dopóki ten konstruktor nie odpali) nie
będzie class not found. Innymi słowy, ta klasa musi jakoś ładować ten
fragment z saxonem (tak jak wspomniałem -- deklaracja, konstruktor lub
statyczny inicjalizer).

Dawid

test.jar

Ula K.

unread,
Feb 23, 2011, 9:31:11 AM2/23/11
to jug-p...@googlegroups.com
Dawid, a spróbuj z innej klasy załadować Twoją klasę test.A uzywając Class.forName(). Jeśli dobrze Tomka zrozumiałam, to właśnie powoduje problemy.

Ula


--
Otrzymujesz tę wiadomość, ponieważ subskrybujesz grupę dyskusyjną Google o nazwie "Poznań Java User Group".

Aby zamieszczać posty w tej grupie, wyślij e-mail na adres jug-p...@googlegroups.com.
Aby anulować subskrypcję tej grupy, wyślij e-mail na adres jug-poznan+...@googlegroups.com.
Aby uzyskać więcej informacji, odwiedź tę grupę pod adresem http://groups.google.com/group/jug-poznan?hl=pl.




--
Urszula Krukar
ukr...@gmail.com

Leszek Gawron

unread,
Feb 23, 2011, 9:53:57 AM2/23/11
to jug-p...@googlegroups.com
On 2011-02-23 15:31, Ula K. wrote:
> Dawid, a spr�buj z innej klasy za�adowa� Twoj� klas� test.A uzywaj�c
> Class.forName(). Je�li dobrze Tomka zrozumia�am, to w�a�nie powoduje
> problemy.

no wlasnie nie

a = Class.forName( "A" ); // ok
A obj = (A) a.newInstance(); //ok
a.deref(); // exception

ale za to :

public class A {
public final static Object factory;
static {
factory = new SaxonFactory();
}
}

spowoduje wyjatek juz podczas Class.forName( "A" ) bo samo zaladowanie
klasy powoduje w dalszej kolejnosci inicjalizacje statyczna.

Dawid Weiss

unread,
Feb 23, 2011, 9:56:08 AM2/23/11
to jug-p...@googlegroups.com

forName(String) nie ma znaczenia, bo powoduje jedynie załadowanie klasy do JVM (swoją drogą z forName są inne problemy -- który class loader będzie użyty do załadowania klasy? Odpowiedź jest w wielu przypadkach istotna). Przy załadowaniu klasy resolve robi się tylko na tym, co jest wtedy używane (czyli wywołania klas w kodzie nie).

Jako niewierny swojej wiedzy sprawdziłem i jest tak, jak myślałem:

dweiss@dweiss-main:~/Downloads$ cat C.java

public class C {
  public static void main(String [] args) throws Exception {
    Class.forName("test.A");
    System.out.println("Done.");
  }
}

dweiss@dweiss-main:~/Downloads$ java -cp .:test.jar C
Done.

Dawid



2011/2/23 Ula K. <ukr...@gmail.com>:

Dawid Weiss

unread,
Feb 23, 2011, 9:59:07 AM2/23/11
to jug-p...@googlegroups.com

Słuchajcie, tutaj w zasadzie tajemnicy żadnej nie ma -- ten wyjątek może wystąpić wtedy, gdy w bytecode jest robione getfield, setfield, lub invoke* (albo przy ładowaniu samej deklaracji klasy w momencie, gdy nieznana jest jej hierarchia, interfejsy lub adnotacje).

Przynajmniej tak jest, dopóki mnie ktoś palcem nie przekona, że może być inaczej :) Ula, podeślij mi tę konkretną klasę z ciekawości -- może być bytecode (na prv., żeby nie było).

Dawid


2011/2/23 Leszek Gawron <lga...@mobilebox.pl>
On 2011-02-23 15:31, Ula K. wrote:
Dawid, a spróbuj z innej klasy załadować Twoją klasę test.A uzywając
Class.forName(). Jeśli dobrze Tomka zrozumiałam, to właśnie powoduje
problemy.

no wlasnie nie

a = Class.forName( "A" ); // ok
A obj = (A) a.newInstance(); //ok
a.deref(); // exception

ale za to :

public class A {
       public final static Object factory;
       static {
               factory = new SaxonFactory();
       }
}

spowoduje wyjatek juz podczas Class.forName( "A" ) bo samo zaladowanie klasy powoduje w dalszej kolejnosci inicjalizacje statyczna.

--
Leszek Gawron                         http://www.mobilebox.pl/krs.html
CTO at MobileBox Ltd.

--

Leszek Gawron

unread,
Feb 23, 2011, 10:05:14 AM2/23/11
to jug-p...@googlegroups.com
On 2011-02-23 15:59, Dawid Weiss wrote:
>
> S�uchajcie, tutaj w zasadzie tajemnicy �adnej nie ma -- ten wyj�tek mo�e
> wyst�pi� wtedy, gdy w bytecode jest robione getfield, setfield, lub
> invoke* (albo przy �adowaniu samej deklaracji klasy w momencie, gdy

> nieznana jest jej hierarchia, interfejsy lub adnotacje).
>
> Przynajmniej tak jest, dop�ki mnie kto� palcem nie przekona, �e mo�e by�
> inaczej :) Ula, pode�lij mi t� konkretn� klas� z ciekawo�ci -- mo�e by�
> bytecode (na prv., �eby nie by�o).

http://www.koders.com/java/fid59E5F44DAC70E7215C7FEC821199443FDEA69B5E.aspx?s=ConstEntry

pytanie czy to ta sama wersja, ale jesli tak to instrukcja w linii 288
nie musi byc wykonywana jesli instancja JobEntryXSLT jest odpowiednio
"skonfigurowana".

Tomek Matynia

unread,
Feb 23, 2011, 10:07:33 AM2/23/11
to jug-p...@googlegroups.com
Rzeczywiście w przypadku konstruktora jest tak jak piszesz, zgadzam się.

W sumie odpisywałem w tym samym momencie, kiedy Ula wysłała kod
źródłowy. Chodziło mi o inny przypadek, tak z głowy o coś takiego:

public class A {
public static String TEST = "test";
public static B TEST2 = new B();
}

W przypadku odwołania do A.TEST, a przy braku B na classpath powinien
wystąpić właśnie ten błąd. Ale to tak jak piszę -- zupełnie inny
przypadek.

Pozdrawiam,
Tomek

Leszek Gawron

unread,
Feb 23, 2011, 10:12:21 AM2/23/11
to jug-p...@googlegroups.com
On 2011-02-23 16:07, Tomek Matynia wrote:
> Rzeczywi�cie w przypadku konstruktora jest tak jak piszesz, zgadzam si�.
>
> W sumie odpisywa�em w tym samym momencie, kiedy Ula wys�a�a kod
> �r�d�owy. Chodzi�o mi o inny przypadek, tak z g�owy o co� takiego:

>
> public class A {
> public static String TEST = "test";
> public static B TEST2 = new B();
> }
>
> W przypadku odwo�ania do A.TEST, a przy braku B na classpath powinien
> wyst�pi� w�a�nie ten b��d. Ale to tak jak pisz� -- zupe�nie inny
> przypadek.

nie w przypadku wywolania A.TEST tylko juz w momencie ladowania klasy A.
Klasa ladowana jest inicjalizowana to znaczy:

- tworzone sa zmienne statyczne
- uruchamiane sa bloki kodu static {}


dowod:

public class A {
private final static B BINSTANCE = new B();

public static void main( String[] args ) {
// nothing here
}

public void hello() {
System.out.println( "Hello." );
}
}


D:\mp3\Flogging Molly>java A
Exception in thread "main" java.lang.NoClassDefFoundError: B
at A.<clinit>(A.java:2)
Caused by: java.lang.ClassNotFoundException: B
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 1 more
Could not find the main class: A. Program will exit.

PS. prosze zignorowac mp3/Flogging Molly :)))

Artur Kłopotek

unread,
Feb 23, 2011, 10:15:14 AM2/23/11
to jug-p...@googlegroups.com
> nie w przypadku wywolania A.TEST tylko juz w momencie ladowania klasy A.
> Klasa ladowana jest inicjalizowana to znaczy:
>
> - tworzone sa zmienne statyczne
> - uruchamiane sa bloki kodu static {}
>
>
> dowod:
>
> public class A {
>        private final static B  BINSTANCE       = new B();
>
>        public static void main( String[] args ) {
>                // nothing here
>        }
>
>        public void hello() {
>                System.out.println( "Hello." );
>        }
> }

Niestety przedstawiony kod ma się nijak do tego co napisałeś nad nim
:) Tworzenie instancji to nie to samo co ładowanie klasy.

Pozdrawiam
Artur

Tomek Matynia

unread,
Feb 23, 2011, 10:17:30 AM2/23/11
to jug-p...@googlegroups.com
Kurcze, to był skrót myślowy..

Pozdrawiam,
Tomek

W dniu 23 lutego 2011 16:12 użytkownik Leszek Gawron
<lga...@mobilebox.pl> napisał:


> On 2011-02-23 16:07, Tomek Matynia wrote:
>>

>> Rzeczywiście w przypadku konstruktora jest tak jak piszesz, zgadzam się.
>>
>> W sumie odpisywałem w tym samym momencie, kiedy Ula wysłała kod
>> źródłowy. Chodziło mi o inny przypadek, tak z głowy o coś takiego:


>>
>> public class A {
>>     public static String TEST = "test";
>>     public static B TEST2 = new B();
>> }
>>

>> W przypadku odwołania do A.TEST, a przy braku B na classpath powinien

>> wystąpić właśnie ten błąd. Ale to tak jak piszę -- zupełnie inny

Leszek Gawron

unread,
Feb 23, 2011, 10:18:49 AM2/23/11
to jug-p...@googlegroups.com
On 2011-02-23 16:15, Artur K�opotek wrote:
>> nie w przypadku wywolania A.TEST tylko juz w momencie ladowania klasy A.
>> Klasa ladowana jest inicjalizowana to znaczy:
>>
>> - tworzone sa zmienne statyczne
>> - uruchamiane sa bloki kodu static {}
>>
>>
>> dowod:
>>
>> public class A {
>> private final static B BINSTANCE = new B();
>>
>> public static void main( String[] args ) {
>> // nothing here
>> }
>>
>> public void hello() {
>> System.out.println( "Hello." );
>> }
>> }
>
> Niestety przedstawiony kod ma si� nijak do tego co napisa�e� nad nim
> :) Tworzenie instancji to nie to samo co �adowanie klasy.

a gdzie ja tutaj tworze instancje klasy A?


odpowiadalem na :


> public class A {
> public static String TEST = "test";
> public static B TEST2 = new B();
> }
>
> W przypadku odwo�ania do A.TEST, a przy braku B na classpath powinien
> wyst�pi� w�a�nie ten b��d. Ale to tak jak pisz� -- zupe�nie inny
> przypadek.
>

to jest prawdziwa teza wynikajaca z falszywego zalozenia. Blad powoduje
nie odwolanie A.TEST tylko zaladowanie klasy A, ktora posiada STATYCZNE
POLE typu B, ktora to klasa B nie jest dostepna.


PS. this is fun :)

Tomek Matynia

unread,
Feb 23, 2011, 10:19:04 AM2/23/11
to jug-p...@googlegroups.com
Zresztą napisałem "odwołania do A.TEST", a nie ŁADOWANIA A.TEST.

Pozdrawiam,
Tomek

W dniu 23 lutego 2011 16:17 użytkownik Tomek Matynia
<tomek....@gmail.com> napisał:

Artur Kłopotek

unread,
Feb 23, 2011, 10:21:29 AM2/23/11
to jug-p...@googlegroups.com
> a gdzie ja tutaj tworze instancje klasy A?

Nigdzie. Tworzona jest natomiast instancja klasy B, ponieważ pole
statyczne jest inicjalizowane w momencie odwołania do statycznej
metody main().

Leszek Gawron

unread,
Feb 23, 2011, 10:23:34 AM2/23/11
to jug-p...@googlegroups.com
On 2011-02-23 16:21, Artur K�opotek wrote:
>> a gdzie ja tutaj tworze instancje klasy A?
>
> Nigdzie. Tworzona jest natomiast instancja klasy B, poniewaďż˝ pole
> statyczne jest inicjalizowane w momencie odwo�ania do statycznej
> metody main().

egzaktly, wszyscy rozumieja, skonczmy podw�tek.

Ula, masz tego wiecej? Jak widzisz nar�d sie nudzi :)

Urszula Krukar

unread,
Feb 23, 2011, 11:38:40 AM2/23/11
to jug-p...@googlegroups.com
W dniu 2011-02-23 16:23, Leszek Gawron pisze:

> On 2011-02-23 16:21, Artur K�opotek wrote:
>>> a gdzie ja tutaj tworze instancje klasy A?
>>
>> Nigdzie. Tworzona jest natomiast instancja klasy B, poniewaďż˝ pole
>> statyczne jest inicjalizowane w momencie odwo�ania do statycznej
>> metody main().
>
> egzaktly, wszyscy rozumieja, skonczmy podw�tek.
>
> Ula, masz tego wiecej? Jak widzisz nar�d sie nudzi :)
>
Chwilowo nie mam, ale jakby co wiem, gdzie was znale�� :)

--
Urszula Krukar
ukr...@gmail.com

Dawid Weiss

unread,
Feb 23, 2011, 12:00:26 PM2/23/11
to jug-p...@googlegroups.com, Leszek Gawron
 
nie w przypadku wywolania A.TEST tylko juz w momencie ladowania klasy A. Klasa ladowana jest inicjalizowana to znaczy:

- tworzone sa zmienne statyczne
- uruchamiane sa bloki kodu static {}

Sprawa jest nieco bardziej skomplikowana jeśli się używa forName(String, boolean, ClassLoader) bo wtedy można ją załadować i nie inicjalizować :) 

Kiedyś też wspominałem, że w zasadzie nie ma "tworzenia zmiennych statycznych" -- bloki kodu będące przypisaniem do zmiennych statycznych lądują w metodzie clinit klasy razem z tym, co jest w static {...} (kompilator łączy wszystkie deklaracje w jedną, w kolejności wystąpień). Dlatego ma znaczenie jeśli ktoś woła z takich bloków (lub przypisań) inne metody statyczne klasy, bo mogą zobaczyć niezainicjalizowane pola final, są zależne od kolejności deklaracji itd.

Co do oryginalnego problemu Uli, to jest dosyć fascynujący, bo ta klasa powinna się załadować, a dopiero wywołanie metody execute() walnąć wyjątek braku Saxona (i zgodnie z tym, co powiedział Leszek -- jeśli jest możliwość konfiguracji, to można to wywołanie pominąć i nie powinno być class not found!).

Ula nie przesłała pełnego stack trace'a (z podwyjątkami), więc pewnie coś ukrywa :) Zobaczcie, wrzuciłem na github:


Tam są wariacje przykładów na temat. Klasy Test1 i Test2 ładują i wołają (przez interfejs, by nie było przedwczesnego ładowania) klasę test.ReferencesSaxon i test.ReferencesSaxon2:

public class Test1
{
    public static void main(String [] args) throws Exception
    {
        ((CallOrNot) Class.forName("test.ReferencesSaxon").newInstance())
            .doCall(args.length > 0);
    }
}

w ReferencesSaxon* jest albo wywołanie Saxona, jak tu:

public class ReferencesSaxon implements CallOrNot
{
    @Override
    public void doCall(boolean call)
    {
        if (call) {
            new net.sf.saxon.TransformerFactoryImpl();
        }
    }

albo w bloku statycznym inicjalizacja:

package test;


public class ReferencesSaxon2 implements CallOrNot
{
    static 
    {
        // this should throw an exception.
        new ReferencesSaxon().doCall(true);
    }
    
    @Override
    public void doCall(boolean call)
    {
        // ignore.
    }

No i mamy, dla przykładu pierwszego, gdy blok if nie jest spełniony:

echo "# Should be o.k."
java -cp tmp Test1 

Przechodzi cacy. Natomiast:

echo "# Should fail."
java -cp tmp Test1 abc 

Wywala:

Exception in thread "main" java.lang.NoClassDefFoundError: net/sf/saxon/TransformerFactoryImpl
        at test.ReferencesSaxon.doCall(ReferencesSaxon.java:10)
        at Test1.main(Test1.java:8)
Caused by: java.lang.ClassNotFoundException: net.sf.saxon.TransformerFactoryImpl
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        ... 2 more

Jak widać, elegancko się wysypało, ale dopiero wtedy, gdy następuje próba załadowania klasy w kodzie, której nie ma (dostępnej w classloaderze kontekstowym dokładnie). Jak też widać, stack trace musi być pełen żeby było widać skąd problem pochodzi (Ula: podeślij mi pełen, z ciekawości).

Drugi przykład to ten z blokiem static, tutaj jest podobnie:

echo "# Should always fail."
java -cp tmp Test2 

kończy się:

# Should always fail.
Exception in thread "main" java.lang.NoClassDefFoundError: net/sf/saxon/TransformerFactoryImpl
        at test.ReferencesSaxon.doCall(ReferencesSaxon.java:10)
        at test.ReferencesSaxon2.<clinit>(ReferencesSaxon2.java:9)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at Test2.main(Test2.java:8)
Caused by: java.lang.ClassNotFoundException: net.sf.saxon.TransformerFactoryImpl
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
        ... 5 more

czyli widać, że forName0() ładuje <clinit> (to jest nazwa metody inicjalizującej klasę) i dalej już jest normalnie.

Pozdrowienia,
Dawid

jmilk...@gmail.com

unread,
Feb 23, 2011, 1:15:41 PM2/23/11
to Poznań Java User Group
Witam

Wszystko to jest w miarę oczywiste zastanawiam się tylko czy w tym
przypadku wołanie tych ReferencesSaxon* przez interfejs czemuś służy?

pzdr miluch

Dawid Weiss

unread,
Feb 23, 2011, 1:30:08 PM2/23/11
to jug-p...@googlegroups.com

Usuń ten interfejs i spróbuj wywołać jakąś metodę tych klas nie przez reflection, to poznasz jego cel.

jmilk...@gmail.com

unread,
Feb 23, 2011, 1:59:30 PM2/23/11
to jug-p...@googlegroups.com
Witam

No chyba cały czas czegoś nie widzę, czemu nie można zrobić tego tak:

test1:
((ReferencesSaxon1) Class.forName("saxons.ReferencesSaxon1").newInstance())
.doCall(args.length > 0);

test2:
((ReferencesSaxon2) Class.forName("saxons.ReferencesSaxon2").newInstance())
.doCall(args.length > 0);

pzdr miluch

W dniu 23 lutego 2011 19:30 użytkownik Dawid Weiss
<dawid...@cs.put.poznan.pl> napisał:
>

Urszula Krukar

unread,
Feb 23, 2011, 2:03:13 PM2/23/11
to jug-p...@googlegroups.com
W dniu 2011-02-23 18:00, Dawid Weiss pisze:
ďż˝
nie w przypadku wywolania A.TEST tylko juz w momencie ladowania klasy A. Klasa ladowana jest inicjalizowana to znaczy:

- tworzone sa zmienne statyczne
- uruchamiane sa bloki kodu static {}

Sprawa jest nieco bardziej skomplikowana je�li si� u�ywa forName(String, boolean, ClassLoader) bo wtedy mo�na j� za�adowa� i nie inicjalizowa� :)�

Kiedy� te� wspomina�em, �e w zasadzie nie ma "tworzenia zmiennych statycznych" -- bloki kodu b�d�ce przypisaniem do zmiennych statycznych l�duj� w metodzie clinit klasy razem z tym, co jest w static {...} (kompilator ��czy wszystkie deklaracje w jedn�, w kolejno�ci wyst�pie�). Dlatego ma znaczenie je�li kto� wo�a z takich blok�w (lub przypisa�) inne metody statyczne klasy, bo mog� zobaczy� niezainicjalizowane pola final, s� zale�ne od kolejno�ci deklaracji itd.

Co do oryginalnego problemu Uli, to jest dosy� fascynuj�cy, bo ta klasa powinna si� za�adowa�, a dopiero wywo�anie metody execute() waln�� wyj�tek braku Saxona (i zgodnie z tym, co powiedzia� Leszek -- je�li jest mo�liwo�� konfiguracji, to mo�na to wywo�anie pomin�� i nie powinno by� class not found!).

Ula nie przes�a�a pe�nego stack trace'a (z podwyj�tkami), wi�c pewnie co� ukrywa :)
Oj tam zaraz. To co poda�am to by� najg��bszy podwyjatek.

Co do oryginalnego problemu: ten kod jest uruchamiany na JBossie w ramach webapp. Tam hierarchia classloader�w jest dosy� skomplikowana, to pewnie stanowi jakie� rozwi�zanie tej zagadki.

-- 
Urszula Krukar
ukr...@gmail.com

Dawid Weiss

unread,
Feb 23, 2011, 2:09:45 PM2/23/11
to jug-p...@googlegroups.com

Bo wtedy to cast pewnie załaduje klasę i forName nic nie zrobi, a chciałem, by nie było wątpliwości.

Dawid Weiss

unread,
Feb 23, 2011, 2:12:58 PM2/23/11
to jug-p...@googlegroups.com
 
Oj tam zaraz. To co podałam to był najgłębszy podwyjatek.

Co do oryginalnego problemu: ten kod jest uruchamiany na JBossie w ramach webapp. Tam hierarchia classloaderów jest dosyć skomplikowana, to pewnie stanowi jakieś rozwiązanie tej zagadki.

Hierarchia classloaderów nie powinna mieć wpływu -- gdzieś to się w końcu pojawia. No chyba, że ten wyjątek jest propagowany. W każdym razie to jest dosyć dziwne, bo ta klasa powinna się załadować, przynajmniej nie widzę przyczyny czemu by nie miała.

Dojdź do tego co się dzieje i zrób prezentację na JUGu. :)

Dawid

jmilk...@gmail.com

unread,
Feb 23, 2011, 2:18:47 PM2/23/11
to jug-p...@googlegroups.com
W przypadku test2
Class<?> sss = Class.forName("saxons.ReferencesSaxon2");

to już powoduje (przynajmniej u mnie) załadowanie klasy i wyjątek

a dla test1 teoretycznie załadowanie klasy i tak nie powoduje wyjątku.
U mnie StackTrace są identyczne niezależnie od tego czy mam interfejs czy nie.

pzdr miluch


W dniu 23 lutego 2011 20:09 użytkownik Dawid Weiss

Dawid Weiss

unread,
Feb 23, 2011, 2:59:41 PM2/23/11
to jug-p...@googlegroups.com

Masz rację -- po chwili przemyślenia doszedłem do wniosku, że to będzie to samo, bo fizycznie cast jest w bytecode po forName(), ale dla większości ludzi wyrażenie (X) Class.forName(X) będzie sugerowało, że klasa (X) jest jakoś ładowana w klasie, która to deklaruje, a chciałem być jednoznaczny.

Ula K.

unread,
Feb 24, 2011, 2:42:11 AM2/24/11
to jug-p...@googlegroups.com
Dla ciągle zainteresowanych przesyłam pełen stack trace. :)
To nie jest stack z nazej webapp, tylko z toola do uruchamiania jobów Kettle, ale wyjatek jest ten sam.

Ula


Dawid

--
Otrzymujesz tę wiadomość, ponieważ subskrybujesz grupę dyskusyjną Google o nazwie „Poznań Java User Group”.
Aby zamieszczać posty w tej grupie, wyślij e-mail na adres jug-p...@googlegroups.com.
Aby anulować subskrypcję tej grupy, wyślij e-mail na adres jug-poznan+...@googlegroups.com.
Aby uzyskać więcej informacji, odwiedź tę grupę pod adresem http://groups.google.com/group/jug-poznan?hl=pl.



--
Urszula Krukar
ukr...@gmail.com
noclassdeffound.txt

Dawid Weiss

unread,
Feb 24, 2011, 3:28:28 AM2/24/11
to jug-p...@googlegroups.com
> Dla ciągle zainteresowanych przesyłam pełen stack trace. :)
> To nie jest stack z nazej webapp, tylko z toola do uruchamiania jobów Kettle, ale wyjatek jest ten sam.

To nie jest chyba pełen wyjątek, Ula -- dlatego nie widać gdzie dokładnie
idzie ścieżka wywołań... a przynajmniej tak mi się wydaje... Ostatnie,
co widać bierze się stąd:

http://source.pentaho.org/viewvc/svnkettleroot/Kettle/trunk/src/org/pentaho/di/job/entry/JobEntryCopy.java?annotate=12794

w linii 121-125 jest:

} catch (Throwable e) {
String message = "Unable to read Job Entry copy info from XML node :
" + e.toString();
throw new KettleXMLException(message, e);
}

PluginRegistry, który rzuca wyjątek wywalany dalej stąd robi zaś:

catch (Throwable e)
{
e.printStackTrace();
throw new KettlePluginException(BaseMessages.getString(PKG,
"PluginRegistry.RuntimeError.UnExpectedErrorLoadingClass.PLUGINREGISTRY007"),
e);
}

Ech... pamiętacie wykład o debugowaniu? Przekleństwo
e.printStackTrace! W każdym razie wyjątek zagnieżdżony niby wygląda
tak:

Caused by: java.lang.NoClassDefFoundError: net/sf/saxon/TransformerFactoryImpl


at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)

at org.pentaho.di.core.plugins.PluginRegistry.loadClass(PluginRegistry.java:297)
... 35 more
Caused by: java.lang.ClassNotFoundException: net.sf.saxon.TransformerFactoryImpl
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:303)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:316)
... 38 more

co by jednak sugerowało, że gdzieś tam w środku coś tego saxona
potrzebuje w trakcie ładowania... ale co to jest -- doprawdy nie wiem
i nie wiem dlaczego nie widać żadnej metody <clinit> w tym stosie,
choć powinno być.
Ciekawy problem, z chęcią bym się pobawił. Można jeszcze pobadać, np. przez:

-XX:+TraceClassLoading

i zobaczyć jakie klasy się ładują po kolei, albo sprawdzić czy to na
pewno ta klasa, o której była mowa się wtedy ładuje. Bez żywego kodu
to jest zgadywanie, ale jeśli mi udostępnisz remote desktop... :D

D.

Ula K.

unread,
Feb 24, 2011, 3:54:32 AM2/24/11
to jug-p...@googlegroups.com
Instrukcja będzie musiała wystarczyć:
- ściągnij http://sourceforge.net/projects/pentaho/files/Data%20Integration/4.1.0-stable/pdi-ce-4.1.0-stable.zip/download
- rozpakuj :)
- w katalogu libext jest saxon8.jar, zmień nazwę, żeby nie było go na classpathie
- uruchom spoon.sh
- w aplikacji wykona z menu File -> Import from XML file i wybierz plik z załącznika
- dostaniesz śliczny stack trace :)

Ula

--
Otrzymujesz tę wiadomość, ponieważ subskrybujesz grupę dyskusyjną Google o nazwie "Poznań Java User Group".

Aby zamieszczać posty w tej grupie, wyślij e-mail na adres jug-p...@googlegroups.com.
Aby anulować subskrypcję tej grupy, wyślij e-mail na adres jug-poznan+...@googlegroups.com.
Aby uzyskać więcej informacji, odwiedź tę grupę pod adresem http://groups.google.com/group/jug-poznan?hl=pl.




--
Urszula Krukar
ukr...@gmail.com
classnotfound.kjb

Dawid Weiss

unread,
Feb 24, 2011, 3:58:47 AM2/24/11
to jug-p...@googlegroups.com
Kocham Cię :) Pobawię się później i dam znać co wyszło.

D.

2011/2/24 Ula K. <ukr...@gmail.com>:

jmilk...@gmail.com

unread,
Feb 24, 2011, 4:39:33 AM2/24/11
to Poznań Java User Group
Witam

On 24 Lut, 09:28, Dawid Weiss <dawid.we...@cs.put.poznan.pl> wrote:
> > Dla ciągle zainteresowanych przesyłam pełen stack trace. :)
> > To nie jest stack z nazej webapp, tylko z toola do uruchamiania jobów Kettle, ale wyjatek jest ten sam.
>
> To nie jest chyba pełen wyjątek, Ula -- dlatego nie widać gdzie dokładnie
> idzie ścieżka wywołań... a przynajmniej tak mi się wydaje... Ostatnie,
> co widać bierze się stąd:
>
> http://source.pentaho.org/viewvc/svnkettleroot/Kettle/trunk/src/org/p...
>
> w linii 121-125 jest:
>
> } catch (Throwable e) {
>
>   String message = "Unable to read Job Entry copy info from XML node :
> " + e.toString();
>   throw new KettleXMLException(message, e);
>
> }
>
> PluginRegistry, który rzuca wyjątek wywalany dalej stąd robi  zaś:
>
> catch (Throwable e)
> {
> e.printStackTrace();
> throw new KettlePluginException(BaseMessages.getString(PKG,
> "PluginRegistry.RuntimeError.UnExpectedErrorLoadingClass.PLUGINREGISTRY007"),
> e);
>
> }
>
> Ech... pamiętacie wykład o debugowaniu? Przekleństwo
> e.printStackTrace!
To może w takim razie dla osób które nie były/nie pamiętają małe
wyaśnienie w tej sprawie?

> W każdym razie wyjątek zagnieżdżony niby wygląda tak:
>
> Caused by: java.lang.NoClassDefFoundError: net/sf/saxon/TransformerFactoryImpl
>         at java.lang.Class.forName0(Native Method)
>         at java.lang.Class.forName(Class.java:169)
>         at org.pentaho.di.core.plugins.PluginRegistry.loadClass(PluginRegistry.java:297)
>         ... 35 more
> Caused by: java.lang.ClassNotFoundException: net.sf.saxon.TransformerFactoryImpl
>         at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
>         at java.security.AccessController.doPrivileged(Native Method)
>         at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
>         at java.lang.ClassLoader.loadClass(ClassLoader.java:303)
>         at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
>         at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:316)
>         ... 38 more
>
> co by jednak sugerowało, że gdzieś tam w środku coś tego saxona
> potrzebuje w trakcie ładowania... ale co to jest -- doprawdy nie wiem
> i nie wiem dlaczego nie widać żadnej metody <clinit> w tym stosie,
> choć powinno być.
> Ciekawy problem, z chęcią bym się pobawił. Można jeszcze pobadać, np. przez:
>
> -XX:+TraceClassLoading
>
> i zobaczyć jakie klasy się ładują po kolei, albo sprawdzić czy to na
> pewno ta klasa, o której była mowa się wtedy ładuje. Bez żywego kodu
> to jest zgadywanie, ale jeśli mi udostępnisz remote desktop... :D
>
> D.

pzdr miluch

Dawid Weiss

unread,
Feb 24, 2011, 5:43:59 AM2/24/11
to jug-p...@googlegroups.com
> To może w takim razie dla osób które nie były/nie pamiętają małe
> wyaśnienie w tej sprawie?

Zrobimy repetę wykładu za jakiś czas, to przypomnę :)

Dawid

Dawid Weiss

unread,
Feb 24, 2011, 8:36:56 AM2/24/11
to jug-p...@googlegroups.com, Ula K., Stanislaw Osinski
Dla tych, którzy jeszcze słuchają... :)

Okazuje się, że jest to bardzo ciekawy przypadek, którego szczerze
mówiąc w ogóle nie brałem pod uwagę, a który mnie bardzo zaskoczył i
czegoś nowego nauczył. A więc jest tak.

1. Klasą, która powoduje oryginalny wyjątek u Uli jest JobEntryXSLT i
faktycznie w niej referencja do Saxona:

factory = new net.sf.saxon.TransformerFactoryImpl();

2. Powyższy kod nigdy nie jest wykonywany, ale powoduje
ClassNotFoundError w forName().

3. Te przykłady, które podawałem również są poprawne (i działają, co
widzieliśmy).

Czym owe przypadki się różnią? Otóż różnią się tym, że JVM musi
przeprowadzić proces weryfikacji kodu, między innymi zgodności
przypisań. Ten fragment w oryginale klasy JobEntryXSLT jest taki:

// Create transformer factory
TransformerFactory factory = TransformerFactory.newInstance();

if (xsltfactory.equals(FACTORY_SAXON))
{
// TODO: replace the active line with this code and
// forName() will pass just fine

// Object temporary = new net.sf.saxon.TransformerFactoryImpl();
factory = new net.sf.saxon.TransformerFactoryImpl();
}

Ponieważ TransformerFactory to interfejs, więc JVM chce sprawdzić, czy
TransformerFactoryImpl ten interfejs na pewno implementuje. To z kolei
powoduje próbę załadowania klasy (bez jej inicjalizacji nawet) i wtedy
wszystko idzie w maliny. Można to sprawdzić, jeśli się zrobi taki
"stub" klasy TransformerFactoryImpl, który nie implementuje
TransformerFactory... wynik jest taki (Class.forName, bez żadnej
inicjalizacji!)

Exception in thread "main" java.lang.VerifyError: (class:
org/pentaho/di/job/entries/xslt/JobEntryXSLT, method:
processOneXMLFile signature:
(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/pentaho/di/core/Result;Lorg/pentaho/di/job/Job;)Z)
Incompatible object argument for function call

Tadaaa.

Uwaga, teraz najlepsze: zmiana powyższego fragmentu na Object lub typ
dokładny powoduje, że weryfikacja przechodzi bez ładowania klasy
TransformerFactoryImpl (a więc przechodzi bez classnotfound elegancko
tak, jak byśmy tego oczekiwali). Podobnie jest, gdy się doda cast
explicite na TransformerFactory:

factory = (TransformerFactory) new net.sf.saxon.TransformerFactoryImpl();

i znów -- wszystko ładnie się ładuje i przechodzi.

Fajne, nie? :)

Żeby być kompletnym sprawdziłem w JVM spec jak dokładnie przebiega
ładowanie klas. Cytat z p. 2.17.3:

"For example, an implementation may choose to resolve each symbolic
reference in a class or interface individually, only when it is used
(lazy or late resolution), or to resolve them all at once, for
example, while the class is being verified (static resolution). This
means that the resolution process may continue, in some
implementations, after a class or interface has been initialized"

Innymi słowy reguły nie ma: może być albo lazy, albo eager
initialization. Sprawdziłem na JRockit, HotSpocie i J9 IBMa i wszędzie
było identycznie, tzn. weryfikacja przypisania powoduje class not
found, jeśli typy mogą być uzgodnione bez znajomości klasy docelowej,
wszystko jest ok.

Ciekawy, pouczający przykład. Jeśli by mi tylko teraz ktoś zapłacił za
ten czas, który mi to zajęło, argh.

Pozdrowienia,
Dawid

jmilk...@gmail.com

unread,
Feb 25, 2011, 6:35:25 AM2/25/11
to Poznań Java User Group
Witam

Wygląda na to, że sprawa została dzięki Dawidowi rozwiązana. Chciałbym
jeszcze zwrócić uwagę na rozwiązanie Uli - podegranie pustych plików
javax.xml.transform.TransformerFactory do META-INF/services w war.
Wydaję mi się że nie jest to coś na czym można polegać w 100%.

Około 2 lata temu miałem podobny problem na WAS gdzie po jednym z
restartów, TransformerFactory za pomocą Services API zaczęło ładować
inną implementację niż ta ładowana dotychczas. W przypadku gdy mamy na
classpath więcej niż 1 plik definiujący providera a ładowanie jest
robione przez classloader.getResourceAsStream(name) (tak jak jest to
robione w przypadku javax.xml.transform.TransformerFactory )to wynik
może być trudny do przewidzenia. Jeśli chciałbym aby saxon na pewno
nie był wykorzystywany to chyba najpewniej jest wywalić z jego jara
plik META-INF/services/javax.xml.transform.TransformerFactory

pzdr miluch

Ula K.

unread,
Feb 25, 2011, 6:58:58 AM2/25/11
to jug-p...@googlegroups.com
Niestety, kolega miluch ma rację.

Na 6 osób w zespole, jednej osobie to rozwiązanie nie działa :( Podejrzewamy wersję maszyny wirtualnej, ale jeszcze rozwiązania nie znależliśmy.

Ula


--
Otrzymujesz tę wiadomość, ponieważ subskrybujesz grupę dyskusyjną Google o nazwie "Poznań Java User Group".

Aby zamieszczać posty w tej grupie, wyślij e-mail na adres jug-p...@googlegroups.com.
Aby anulować subskrypcję tej grupy, wyślij e-mail na adres jug-poznan+...@googlegroups.com.
Aby uzyskać więcej informacji, odwiedź tę grupę pod adresem http://groups.google.com/group/jug-poznan?hl=pl.




--
Urszula Krukar
ukr...@gmail.com

Artur Keska

unread,
Feb 25, 2011, 7:19:00 AM2/25/11
to jug-p...@googlegroups.com
Tak z ciekawości:
Wiesz (albo ktoś może wie) jakim classloaderem jest czytany META-INF/services?

Może inaczej: jeżeli pobranym z current thread to rozwiązanie powinno działać, jeżeli pobranym z Class będzie tak jak
piszesz nieprzewidywalne. Nie wiem też czy przestawiliście izolacje classloaderów więcej tutaj: http://community.jboss.org/wiki/classloadingconfiguration.
Zobacz też co piszą o samej konstrukcji ClassLoaderów (a szczególnie na końcu artykułu) u JBossów: http://docs.jboss.org/jbossweb/3.0.x/class-loader-howto.html

Ja miałem podobny problem na JBoss i ustawienie izolacji classloaderów w EAR'rze oraz ustawnienie w META-INF/services/META-INF\services\javax.xml.parsers.DocumentBuilderFactory (akurat z tym był problem) pomogło.

jmilk...@gmail.com

unread,
Feb 25, 2011, 7:29:08 AM2/25/11
to jug-p...@googlegroups.com
Witam

Raz tylko miałem przyjemność tzn nieprzyjemność działania na JBOSS i
tam zachowanie związane z classloadingiem jest albo słabo
udokumentowane albo po prostu nie działa jak napisane. Poza tym wiem
że różnie to działało w zależnośći od wersji JBossa.
Kolejna rzecz którą trzeba wziąść pod uwage to jest to, że nawet jeśli
wiesz dokładnie który classloader zacznie czytać to może on mieć do
wyboru parę plików META-INF/services/* - czyli przykładowo wrzuć do
WEB-INF/lib aplikacji webowej oba jary: saxon i xalan.
Wtedy która implementacja zostanie zaczytana ?

pzdr miluch


W dniu 25 lutego 2011 13:19 użytkownik Artur Keska
<artur...@gmail.com> napisał:

Artur Keska

unread,
Feb 25, 2011, 7:43:56 AM2/25/11
to jug-p...@googlegroups.com
Przyznaję, dokumentacja JBoss jest średnia i właściwie trzeba by zacząć od pytania o wersję, ale z
tego co się orientuję to akurat ani hierarchia ani mechanizm classloaderów nie uległ zmianie.


W dniu 25 lutego 2011 13:29 użytkownik jmilk...@gmail.com <jmilk...@gmail.com> napisał:
Witam

Raz tylko miałem przyjemność tzn nieprzyjemność działania na JBOSS i
tam zachowanie związane z classloadingiem jest albo słabo
udokumentowane albo po prostu nie działa jak napisane. Poza tym wiem
że różnie to działało w zależnośći od wersji JBossa.
Kolejna rzecz którą trzeba wziąść pod uwage to jest to, że nawet jeśli
wiesz dokładnie który classloader zacznie czytać to może on mieć do
wyboru parę plików META-INF/services/* - czyli  przykładowo wrzuć do
WEB-INF/lib  aplikacji webowej oba jary: saxon i xalan.
Wtedy która implementacja zostanie zaczytana ?

Wg, mnie tu nie chodzi o to które jary zostaną przeczytane ale która konfiguracja services zostanie przeczytana.
To akurat jest opisany w dokumentacji którą podesłałem. To wszystko niestety działa o tyle dobrze o ile poprawnie
napisane zostały fabryki (jakich classloaderów używają).

Dawid Weiss

unread,
Feb 25, 2011, 8:09:35 AM2/25/11
to jug-p...@googlegroups.com
>> WEB-INF/lib  aplikacji webowej oba jary: saxon i xalan.
>> Wtedy która implementacja zostanie zaczytana ?

Nie ma możliwości stwierdzenia, bo to zależy od kolejności, w jakiej
system plików zwróci te JARy do classloadera aplikacji webowej. Dla
systemów plików, które mają alfabetyczny iterator kolejność będzie
stała i przewidywalna :)

To jednak jest nie tyle błąd systemowy, ile logiczny, bo masz dwóch
dostawców tego samego serwisu w aplikacji; pytanie, czy to ma sens.

Dawid

Artur Keska

unread,
Feb 25, 2011, 8:44:50 AM2/25/11
to jug-p...@googlegroups.com

No i właśnie pytanie tylko czy gdzieś jeszcze jest services w którym z jarów (chyba że coś przeoczyłem w dyskusji - sporo tego ;-) )?

Z resztą patrzę sobie w kod od JB i widzę, że classloader (ten z currenThread) akurat najpierw przegląda repositoria a dopiero potem jary więc powinno działać tak jak mówię. Nie wiem z resztą, może ktoś ma ochotę to dogłębniej przeanalizować bo mogę się mylić: http://www.docjar.com/html/api/org/apache/catalina/loader/WebappClassLoader.java.html


 
Dawid

Dawid Weiss

unread,
Feb 25, 2011, 8:54:53 AM2/25/11
to jug-p...@googlegroups.com
Zawsze można sprawdzić skąd go bierze przez (w aplikacji webowej):

log(Thread.currentThread().getContextClassLoader().getResource("META-INF/...").toString())

powinien być URI do pierwszego pasującego zasobu (można też
przeiterować po wszystkich i dowiedzieć się ile w ogóle ich jest i
skąd). Trudno mi powiedzieć jak robi resource lookup JBoss, bo nigdy
go nie używałem, ale powinien być zgodny z servlet API (czyli izolować
aplikacje webowe i robić inwersję poszukiwania -- najpierw WARa i jego
zasoby, później zasoby współdzielone).

Dawid

2011/2/25 Artur Keska <artur...@gmail.com>:

jmilk...@gmail.com

unread,
Feb 25, 2011, 9:16:06 AM2/25/11
to jug-p...@googlegroups.com
Witam

W dniu 25 lutego 2011 14:44 użytkownik Artur Keska
<artur...@gmail.com> napisał:


> W dniu 25 lutego 2011 14:09 użytkownik Dawid Weiss
> <dawid...@cs.put.poznan.pl> napisał:
>>
>> >> WEB-INF/lib aplikacji webowej oba jary: saxon i xalan.
>> >> Wtedy która implementacja zostanie zaczytana ?
>>
>> Nie ma możliwości stwierdzenia, bo to zależy od kolejności, w jakiej
>> system plików zwróci te JARy do classloadera aplikacji webowej. Dla
>> systemów plików, które mają alfabetyczny iterator kolejność będzie
>> stała i przewidywalna :)
>>
>> To jednak jest nie tyle błąd systemowy, ile logiczny, bo masz dwóch
>> dostawców tego samego serwisu w aplikacji; pytanie, czy to ma sens.
>>
>
> No i właśnie pytanie tylko czy gdzieś jeszcze jest services w którym z jarów
> (chyba że coś przeoczyłem w dyskusji - sporo tego ;-) )?
>

Jak rozumiem jeden jest w saxon.jar a drugi to ten stworzony przez
Ulę: siedzi w WEB-INF/classes
(zgodnie z jej opisem dodała dwa pliki: jeden wyląduje w
WEB-INF/classes a drugi bezpośrednio w META-INF wara - by trzeba by
sprawdzić czy ten plik też nie zostanie wzięty pod uwagę przez
classloader) .

pzdr miluch

jmilk...@gmail.com

unread,
Feb 25, 2011, 9:24:40 AM2/25/11
to jug-p...@googlegroups.com
Witam

W dniu 25 lutego 2011 14:09 użytkownik Dawid Weiss
<dawid...@cs.put.poznan.pl> napisał:


>>> WEB-INF/lib  aplikacji webowej oba jary: saxon i xalan.
>>> Wtedy która implementacja zostanie zaczytana ?
>
> Nie ma możliwości stwierdzenia, bo to zależy od kolejności, w jakiej
> system plików zwróci te JARy do classloadera aplikacji webowej. Dla
> systemów plików, które mają alfabetyczny iterator kolejność będzie
> stała i przewidywalna :)

Jesteś pewien że na czymś takim można polegać ? W apliacji standalone
kolejność jarów na classpath ma znacznie - znaleziony zostnanie
pierwszy w kolejności.
Jednak gdy działamy w kontenerze, np JBOSS to classloader contextowy
został (który zostanie wybrany do wyszukania pliku
META-INF/services/*) został napisany przez vendora i to on sobie
określił jaka będzie kolejność.

> To jednak jest nie tyle błąd systemowy, ile logiczny, bo masz dwóch
> dostawców tego samego serwisu w aplikacji; pytanie, czy to ma sens.

Moim zdaniem nie ma: dlatego w moim problemie wywaliłem plik
META-INF/services/* którego nie chciałem. Minus jest taki że taka
zmiana "psuje" tzn moddyfikuje artefakt, a to ma znaczenie gdy
ciągniesz jary z repozytorium mavena.

pzdr miluch

> Dawid

Artur Keska

unread,
Feb 25, 2011, 9:34:50 AM2/25/11
to jug-p...@googlegroups.com
W dniu 25 lutego 2011 15:24 użytkownik jmilk...@gmail.com <jmilk...@gmail.com> napisał:
Witam

W dniu 25 lutego 2011 14:09 użytkownik Dawid Weiss
>>> WEB-INF/lib  aplikacji webowej oba jary: saxon i xalan.
>>> Wtedy która implementacja zostanie zaczytana ?
>
> Nie ma możliwości stwierdzenia, bo to zależy od kolejności, w jakiej
> system plików zwróci te JARy do classloadera aplikacji webowej. Dla
> systemów plików, które mają alfabetyczny iterator kolejność będzie
> stała i przewidywalna :)

Jesteś pewien że na czymś takim można polegać ? W apliacji standalone
kolejność jarów na classpath ma znacznie -  znaleziony zostnanie
pierwszy w kolejności.

Oczywiście że nie można (wydaje mi się że Dawid też tak myśli).
 
Jednak gdy działamy w kontenerze, np JBOSS to classloader contextowy
został (który zostanie wybrany do wyszukania pliku
META-INF/services/*) został napisany przez vendora i to on sobie
określił jaka będzie kolejność.

Dokładnie tak, dla tego radzę Uli, żeby spróbowała przestawić izolację (z tego co wyczytałem Ula używa JB
tylko nie wiem której wersji).
 

> To jednak jest nie tyle błąd systemowy, ile logiczny, bo masz dwóch
> dostawców tego samego serwisu w aplikacji; pytanie, czy to ma sens.

Moim zdaniem nie ma: dlatego w moim problemie wywaliłem plik
META-INF/services/* którego nie chciałem. Minus jest taki że taka
zmiana  "psuje"  tzn moddyfikuje artefakt, a to ma znaczenie gdy
ciągniesz jary z repozytorium mavena.


Zawsze można go na etapie assamblacji mavenowej wywalić ale strach się bać takiego poma :-)

Artur Keska

unread,
Feb 25, 2011, 9:36:38 AM2/25/11
to jug-p...@googlegroups.com
W dniu 25 lutego 2011 14:54 użytkownik Dawid Weiss <dawid...@cs.put.poznan.pl> napisał:
Zawsze można sprawdzić skąd go bierze przez (w aplikacji webowej):

log(Thread.currentThread().getContextClassLoader().getResource("META-INF/...").toString())


Niestety jeżeli jest to proces nieprzewidywalny wówczas możemy dostać poprawny rezultat przypadkowo.
 
powinien być URI do pierwszego pasującego zasobu (można też
przeiterować po wszystkich i dowiedzieć się ile w ogóle ich jest i
skąd). Trudno mi powiedzieć jak robi resource lookup JBoss, bo nigdy
go nie używałem, ale powinien być zgodny z servlet API (czyli izolować
aplikacje webowe i robić inwersję poszukiwania -- najpierw WARa i jego
zasoby, później zasoby współdzielone).

To akurat jest raczej tak jak mówisz, z tą różnicą, że przed jarami czytane jest repozytory (w JB) a więc zawartość EAR'a albo WAR'a.
Z resztą jeżeli mówimy o JBoss jako platformie to ja preferuje jako kontenera aplikacji używać ear'ów a nie warów.

jmilk...@gmail.com

unread,
Feb 25, 2011, 9:41:05 AM2/25/11
to jug-p...@googlegroups.com
W dniu 25 lutego 2011 15:36 użytkownik Artur Keska
<artur...@gmail.com> napisał:
>
>

> W dniu 25 lutego 2011 14:54 użytkownik Dawid Weiss
> <dawid...@cs.put.poznan.pl> napisał:
>>
>> Zawsze można sprawdzić skąd go bierze przez (w aplikacji webowej):
>>
>>
>> log(Thread.currentThread().getContextClassLoader().getResource("META-INF/...").toString())
>>
>
> Niestety jeżeli jest to proces nieprzewidywalny wówczas możemy dostać
> poprawny rezultat przypadkowo.
>
>>
>> powinien być URI do pierwszego pasującego zasobu (można też
>> przeiterować po wszystkich i dowiedzieć się ile w ogóle ich jest i
>> skąd). Trudno mi powiedzieć jak robi resource lookup JBoss, bo nigdy
>> go nie używałem, ale powinien być zgodny z servlet API (czyli izolować
>> aplikacje webowe i robić inwersję poszukiwania -- najpierw WARa i jego
>> zasoby, później zasoby współdzielone).
>
> To akurat jest raczej tak jak mówisz, z tą różnicą, że przed jarami czytane
> jest repozytory (w JB) a więc zawartość EAR'a albo WAR'a.

Chyba nie do końca rozumiem co masz na myśli ? Przed którymi jarami ?

pzdr miluch

Dawid Weiss

unread,
Feb 25, 2011, 10:01:24 AM2/25/11
to jug-p...@googlegroups.com
> Jednak gdy działamy w kontenerze, np JBOSS to classloader contextowy
> został (który zostanie wybrany do wyszukania pliku
> META-INF/services/*) został napisany przez vendora i to on sobie
> określił jaka będzie kolejność.

W przypadku kontenerów aplikacji kolejność przeszukiwania klas jest
określona w specyfikacji (miałeś to na zajęciach ze mną, przypominam
więc tylko). Jeśli kontener ma inne rozszerzenia, to są one dodatkiem
(niezgodnym ze specyfikacją) i powinny być określone w jego
dokumentacji.

Dawid

jmilk...@gmail.com

unread,
Feb 25, 2011, 10:58:52 AM2/25/11
to jug-p...@googlegroups.com
Witam

Dzięki za informację. Ale zajęc z Tobą na ten temat nie miałem -
chodziłem tylko na przedmiot na którym była CORBA, głownie CORBA :)
Z tego co pamiętam to specyfikacja mówi o tym że WEB-INF/classes są
ładowane przed WEB-INF/lib ale wątpię aby zdefiniowany był porządek
ładowania klas pośród tych zawartych w jarach w WEB-INF/lib.

pzdr miluch


W dniu 25 lutego 2011 16:01 użytkownik Dawid Weiss
<dawid...@cs.put.poznan.pl> napisał:

Dawid Weiss

unread,
Feb 25, 2011, 11:37:25 AM2/25/11
to jug-p...@googlegroups.com
A to przepraszam, myślałem, że na TPSI chodziłeś również. Nie mówię o
przypadkach zdegenerowanych -- kolejność wewnątrz WEB-INF/lib nie ma
znaczenia, bo nie powinno tam być > 1 service provider (to Twój błąd
konfiguracji jeśli tak jest). Mówiłem o kolejności względem folderów
wspólnych kontenera i innych lokalizacji.

Ula, podaj to property które wymusza service lookup, bo ja też nie
pamiętam, a to chyba utnie dyskusję...

D.

2011/2/25 jmilk...@gmail.com <jmilk...@gmail.com>:

Ula K.

unread,
Feb 28, 2011, 3:04:10 AM2/28/11
to jug-p...@googlegroups.com
Update:
Property to -Djavax.xml.transform.TransformerFactory=nazwa.klasy

W piątek wychodząc z pracy miałam jeszcze krótka rozmowę z miluchem i wpadliśmy na pomysł, czemu jednaj osobie nie działa rozwiązanie z META-INF/service.
Rzecz w tym, że ja ten katalog wrzuciłam do src/main/webapp, a ten katalog chyba nie jest dodawany do classpatha. Przerzuciliśmy go do src/main/resources, po kompilacji jest w WEB-INF/classes i teraz wszystko smiga.

Ula
Urszula Krukar
ukr...@gmail.com

Pawel Debski

unread,
Mar 7, 2011, 5:09:49 PM3/7/11
to jug-p...@googlegroups.com, Ula K.
Szanowni Państwo!

O ile nie przeoczyłem nie doszło na liście do konkluzji na ten temat i nadal mam jednak kurczę takie poczucie, że jest to klejenie plastrem i podpieranie zapałką. Jak Państwo sądzicie jak powinna być zrobiona biblioteka, żeby uniknąć takich problemów.

Z jednej strony weryfikacja typu w czasie kompilacji jest jak najbardziej właściwa i pożądana i generalnie należy dążyć do tego, żeby jak najwięcej błędów z było wychwytywanych na etapie kompilacji, z drugiej strony biblioteka ma być uniwersalna i pozwalać na konfigurację zewnętrznych zasobów.

Jak Państwa zdaniem powinno to być zrobione?
(jaki wzorzec projektowy jest odpowiedni na taką okazję?)
-- 
  Z powazaniem / Best Regards / 
  Mit freundlichen Gruessen / Meilleures salutations
  Pawel Debski
Managing Consultant, e-mail: PDe...@econsulting.pl
Tel. 0048-504-766-316, 0048-22-730-2794
To join us: cv (o) econsulting (!) pl
To contract us: salesteam (o) econsulting (!) pl
Reply all
Reply to author
Forward
0 new messages