Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[Java] 'Default classloader' instellen

Pagina: 1
Acties:

  • Tuinhark
  • Registratie: April 2000
  • Laatst online: 21-11 21:57
Hoi allen, ik ben een beetje aan het kutten met een classloader en wel om het volgende:

Ik heb een verzameling classes die allemaal een interface implementeren. Deze interface zegt me dat er op de class in kwestie een statische methode beschikbaar is (klopt, dat wordt niet afgedwongen door de interface zelf). Nu zou ik willen dat op het moment dat de class wordt geladen, de statische methode op de class wordt aangeroepen.

Mijn idee was: stel op de één of andere manier een 'default classloader' in zodat al het laden van classes hier langs gaat. Deze classloader kan dan proberen de class te laden (maakt me niet zoveel uit hoe, dus wellicht via de systeemclassloader?) en als dat is gedaan, een Class.isAssignableFrom() aanroep toe te passen, om te checken of de class de interface in kwestie implementeert, etc, etc.

Maar goed, wat ik had bedacht werkt nog niet helemaal. Nu heb ik begrepen dat er in (vanaf?) Java 1.4 een systemproperty beschikbaar is om de defaultclassloader op te geven.. maar ik zit nog vast aan 1.3, helaas.

Anyways, wat ik heb:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import java.lang.reflect.*;
import java.net.*;

public class TranslatingClassLoader extends URLClassLoader
{
  private static final URL[] getUrl()
  {
    try
    {
      return new URL[]
      {
        new URL("file:///s:/classes/")
      };
    }
    catch (MalformedURLException ex)
    {
      return new URL[0];
    }
  }

  public TranslatingClassLoader()
  {
    super(getUrl());
  }

  public TranslatingClassLoader(ClassLoader parent)
  {
    super(getUrl(), parent);
  }

  protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException
  {
    try
    {
      Class c = findClass(name);
        if (resolve)
          resolveClass(c);
        return c;
    }
    catch (ClassNotFoundException ex)
    {
        Class c = super.loadClass(name, resolve);
        return c;
    }
  }

  protected Class findClass(String name) throws ClassNotFoundException
  {
    Class c = super.findClass(name);
    boolean assignable = TranslatableClass.class.isAssignableFrom(c);
    if (assignable)
    {
      try
      {
        Method m = c.getMethod("_translate", new Class[]{Translator.class});
        m.invoke(null, new Object[] {TranslatorImpl.getInstance()});
      }
      catch (Exception ex)
      {
        Logger.log("Could not translate: " + name + " [" + ex.getMessage() + "]", Logger.LEVEL_ERROR);
      }
    }
    return c;
  }
}


Daarbij:

code:
1
2
3
4
5
6
7
public static void main(String[] args)
{
    TranslatingClassLoader loader = new TranslatingClassLoader();
    Class loaderClass = loader.loadClass("menu.Loader");
    java.lang.reflect.Method m = loaderClass.getMethod("main", new Class[] {args.getClass()});
    m.invoke(null, new Object[] {args});
}


Hiermee wordt de main op de class Loader aangeroepen met de argumenten die bij de huidige main-methode waren binnengekomen. Hiermee is het net alsof Loader.main rechtstreeks was aangeroepen, maar nu dus in opdracht van mijn eigen classloader. Verder heb ik geen code aangepast om maar te zorgen/proberen dat de classes via de eigen classloader worden geladen, dus dit is een mate van transparantie die ik zoek.

Naja, voor zover ik kan zien gaat het laden van classes nu inderdaad via mijn eigen classloader en dan zodanig dat als ie een class niet in de directory kan vinden, de benodigde class toch wel wordt gevonden (via de parent loader). Maar op een bepaald moment krijg ik toch een java.lang.LinkageError.

Het gaat misschien te ver om te roepen: "Help, hoe komt dit en hoe los ik dit op?!", maar ik vraag me in ieder geval af of ik op de goede weg ben.

Dus, iemand comments en/of feedback? Doe ik iets doms dat het niet werkt, of is het dusdanig briljant dat ik voorbij de specs van de Java-API aan het opereren ben? :)

Thanks!
:Y)

  • momania
  • Registratie: Mei 2000
  • Laatst online: 23:02

momania

iPhone 30! Bam!

Tuinhark schreef op maandag 17 december 2007 @ 16:58:
Nu zou ik willen dat op het moment dat de class wordt geladen, de statische methode op de class wordt aangeroepen.
Al eens gedacht aan een constructor? :)

Neem je whisky mee, is het te weinig... *zucht*


  • rrrandy
  • Registratie: Juli 2005
  • Laatst online: 27-06 13:00
Wat jij zoekt is AOP (Aspect Oriented Programming). Ik zou zeggen: kijk eens naar AspectJ.

  • momania
  • Registratie: Mei 2000
  • Laatst online: 23:02

momania

iPhone 30! Bam!

rrrandy schreef op maandag 17 december 2007 @ 17:02:
Wat jij zoekt is AOP (Aspect Oriented Programming). Ik zou zeggen: kijk eens naar AspectJ.
Dat kan ook, maar hij wil een methode aanroepen van de class zelf. AOP is dan misschien een beetje overkill?

Neem je whisky mee, is het te weinig... *zucht*


  • rrrandy
  • Registratie: Juli 2005
  • Laatst online: 27-06 13:00
momania schreef op maandag 17 december 2007 @ 17:05:
[...]

Dat kan ook, maar hij wil een methode aanroepen van de class zelf. AOP is dan misschien een beetje overkill?
Dat klopt, maar zoals hij het uitlegt wil hij AOP :P Of het de beste keuze is mag de topic starter zelf achter komen ;)

  • Tuinhark
  • Registratie: April 2000
  • Laatst online: 21-11 21:57
Allen, bedankt voor het meedenken! Voor de duidelijkheid... wat ik wil is dat bij het laden van de class een static method wordt aangeroepen, dus een constructor is geen optie omdat ik dan een object-instantie moet maken. Theoretisch zou ik in de constructor een stukje code kunnen opnemen dat middels een eigen check zorgt dat het éénmalig wordt uitgevoerd (dus quasi-statisch zal ik maar zeggen) of dat ik een static-initializer block gebruik... maar het probleem is dat op die plekken in de code een bepaald gegeven niet voorhanden is, terwijl dat wel benodigd is. De statische methode (zie het code voorbeeldje, r55-56) bevat juist ook een argument waarmee het één-en-ander wordt gedaan.

De insteek is dat ik met zo min mogelijk code aanpassingen mijn doel bereik. Voor mijn gevoel ben ik op de goede weg, maar daar zoek ik dus een klein beetje bevestiging in.

Wat betreft AOP en AspectJ, daar heb ik op zich wel van gehoord, maar nog nooit wat mee gedaan... ik zal dat even moeten bekijken voordat ik daar wat over moet roepen, maar my gut feeling zegt me dat als ik dat zou gaan toepassen, ik flink wat te herschrijven heb. Maar I could be wrong en daarnaast, misschien dat ook zal blijken dat het niet anders kan voor wat ik wil.. ik sluit niets uit.

Voor de goede orde.. Het achtergrondverhaal gaat als volgt: ik heb nu een X-aantal classes die 'vertaald' moeten worden. Daartoe is al een systeem in gebruik en om dat te voorzien wordt nu vanuit een centrale class de static methode direct aangeroepen op alle classes in kwestie. Oftewel, de centrale class heeft een hard-coded lijst van classes die deze moet 'verwerken'. Dat betekent dat als er weer een nieuwe class is toegevoegd en @runtime vertaald moet worden, de lijst in de centrale class moet worden uitgebreid. Niet veel werk natuurlijk, maar kan gemakkelijk vergeten worden. Plus, en dat vind ik erger, dat tijdens startup nu de hele lijst met classes wordt vertaald, zelfs als je specifieke classes tijdens de normale werking van de programmatuur niet eens zou gebruiken! Dus, zonde van de tijd. Ik wil dus nu iets hebben dat dynamisch de vertaling regelt, op het moment dat je de te vertalen class inlaadt (een functie voor het eerst gebruikt).

Ik hoop dat het een beetje duidelijk is!

:Y)

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:14
Ik weet niet of ik het helemaal begrijp, maar volgens mij kan je hier makkelijk gebruik maken van het Spring Framework en het ClassLoader verhaal achterwege laten.
Je kan van de methode die jij door de ClassLoader wilt laten uitvoeren een lifecycle methode maken (init-method) die eenmalig wordt aangeroepen voor de levensduur van het object.
Dat kan allemaal declaratief, dus voor ingrijpende wijzingen hoef je dan niet bang te zijn.

  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Tuinhark schreef op dinsdag 18 december 2007 @ 10:51:
[..] Dus, zonde van de tijd. [..]
Over hoeveel tijd hebben we het hier?

Je eerste bezwaar, dat het gemakkelijk vergeten wordt, lijkt me veel ernstiger, maar om dat te voorkomen kan je gerust on startup alle klassen op het classpath even nalopen op die speciale static methode en die dan uitvoeren. Classloader-magic kan voor een opvolger weleens veel ondoorzichtiger zijn.

Wie trösten wir uns, die Mörder aller Mörder?


  • voodooless
  • Registratie: Januari 2002
  • Laatst online: 18:55

voodooless

Sound is no voodoo!

Je heb je Class al toch? Dan kun je simpelweg zoeken naar de methode die je nodig hebt, en invoke erop aanroepen (met null als obj parameter);

Do diamonds shine on the dark side of the moon :?


  • Salandur
  • Registratie: Mei 2003
  • Laatst online: 18:34

Salandur

Software Engineer

je kan ook een static constructor gebruiken. deze wordt uitgevoerd op het moment dat de class geladen wordt

Java:
1
2
3
4
5
6
7
8
class Salandur {
  private static String staticVar;

  static {
    // dit is de static constructor
   staticVar="geinstantieerd"
  }
}

Assumptions are the mother of all fuck ups | iRacing Profiel


  • BalusC
  • Registratie: Oktober 2000
  • Niet online

BalusC

Carpe diem

een static constructor
Dat wordt een "static initialization block" genoemd.

[ Voor 21% gewijzigd door BalusC op 18-12-2007 13:05 ]


  • Tuinhark
  • Registratie: April 2000
  • Laatst online: 21-11 21:57
Thanks again voor het meedenken.

@BalusC & Salandur: Een static initializer is op dit moment niet gewenst omdat ik dan een flink aantal classes moet herschrijven. Tevens moet 't de static initializer lukken om 'ergens een object vandaan te halen' (het wordt nu als argument in die static methode meegegeven). Dat zou misschien nog best kunnen, maar ik zoek primair naar een 'transparante oplossing', waarbij ik niet honderden classes hoef te herschrijven, wat in principe natuurlijk moet als ik overstap op de static initializer.

edit:

Toevoeging: iedere static initializer zou er trouwens volgens mij in iedere class hetzelfde uit zien en ik ben een klein beetje tegen dit soort copy-pasta code. Als het niet anders kan, nou vooruit.. maar dan alleen na goed verder te hebben gekeken. :)


@voodooless: Je hebt wel gelijk, ware het niet dat dit in feite 'hetzelfde' is als wat ik al heb. Alleen, in plaats van dat ik een lijst met classnames heb, deze lijst bij langs ga, om alle classes te laden en middels reflectie de static methode aan te roepen, doe ik dat rechtstreeks voor alle classes met de instructie classname.staticmethode(arg);. Dat zou in principe zelfs nog efficienter moeten zijn dan het met reflectie te doen! Maar ik heb gemerkt dat ik tegen ClassNotFoundErrors aan loop (ik gok omdat 'ie niet snel genoeg classes kan inladen van schijf -- let wel, ik werk nog met aftandse archaïsche 1.3-technologie). Maar nogmaals, het zou sowieso een rewrite zijn van wat ik nu heb, zonder van 'principe' te veranderen. Dat is nog niet wat ik zoek.

@Confusion: Je hebt op zich wel gelijk dat het vergeten de lijst uit te breiden indien nodig wel bad is, maar daar loop je tijdens het ontwikkelen snel genoeg tegenaan is mijn ervaring. Het punt is dat ik nu met een ronde performancemaatregelen bezig ben in de software. Het inladen van alle classes duurt nu bij startup tientallen seconden. Natuurlijk moet je wachten op die acties, hetzij bij startup, hetzij bij het moment dat je de features die de classes in kwestie gebruiken voor het eerst gebruikt. Ik zou willen kiezen voor de tweede optie omdat ik dan zeker weet dat ik niet onnodig zit te wachten (omdat er geen classes worden geladen voor features die ik toch niet zal gebruiken in mijn sessie). Vandaar dat ik dus een lazy actie zoek (bij gebrek aan een beter woord). :)

@FallenAngel666: Ook van Spring heb ik wel gehoord, maar ook dat heb ik zelf nog niet gebruikt. Misschien dat lifecyclemethodes iets voor me zijn, maar dan moet ik even uitzoeken hoe dat in elkaar steekt. Bedankt voor de suggestie iig.

:Y)

[ Voor 5% gewijzigd door Tuinhark op 18-12-2007 16:03 ]


  • voodooless
  • Registratie: Januari 2002
  • Laatst online: 18:55

voodooless

Sound is no voodoo!

Tuinhark schreef op dinsdag 18 december 2007 @ 16:00:
edit:
Toevoeging: iedere static initializer zou er trouwens volgens mij in iedere class hetzelfde uit zien en ik ben een klein beetje tegen dit soort copy-pasta code.
Misschien kun je wat meer vertellen over wat je methode allemaal doet, want ik ruikt naar een mogelijkheid tot een andere oplossing als je het mij vraagt.

Do diamonds shine on the dark side of the moon :?


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

Tuinhark schreef op dinsdag 18 december 2007 @ 16:00:
Het inladen van alle classes duurt nu bij startup tientallen seconden.
Hebben we het over een desktop of over een serverapplicatie? Die seconden maken weinig uit als de applicatie maar eens in de week opnieuw gestart wordt, wat voor een server app nog veel is. Of is het omdat je tijdens het developen nu telkens lang moet wachten om iets nieuws te kunnen testen/debuggen? Dan is een alternatief ook om met een config file te zorgen dat je uit development builds gewoon hele ritsen classes weglaat. Classloader-fu gaat ongetwijfeld werken, maar ik vraag me af of het de meest overzichtelijke oplossing voor het probleem is.

Wie trösten wir uns, die Mörder aller Mörder?


  • Tuinhark
  • Registratie: April 2000
  • Laatst online: 21-11 21:57
@Confusion: het is een Swing desktopapplicatie. Het is een applicatie die door vele gebruikers bij klanten meerdere malen per dag wordt gestart. (Het zijn verschillende modules die een gezamenlijke codebase hebben.) De grap is dat op bepaalde hardware de applicatie echt bloedjesnel opstart, terwijl op andere plekken het niet voor uit te branden is. Dus daarom ben ik nu aan het zoeken naar knelpunten ("gelukkig" is mijn eigen ontwikkelomgeving ook lekker traag) en het laden van deze enorme lijst van classes duurt mij veel te lang (voor een enkele klal bij het opstarten). Als deze wachttijd kon worden uitgesmeerd over de sessie van de applicatie zouden ik en een aantal klanten daar wel blij van worden. :) Mooi woord trouwens.. Classloader-fu. 3x woordwaarde. :)

@voodooless: Okee, een korte uiteenzetting.

Nu is het zo dat er een Translator-class is. Deze bevat een lijst met classnames die vertaald moeten worden (en dus de static methode _translate(Translator t) bevatten). De Translator loopt over dit lijstje heen en laadt alle classes in. Vervolgens roept 'ie op alle classes de methode aan met 'zichzelf' als argument. In de betreffende methode staan enkele korte instructies zodat resources binnen die class worden vertaald. Dat zijn typisch dingetjes die moeten gebeuren voordat er object instanties worden gemaakt van de class in kwestie. De details doen er feitelijk niet zo toe.

Ik zou er dus naar toe willen dat als de class wordt geladen (via mijn classloader), de classloader de static methode voor me aanroept. Dat betekent dat die Translator niet meer eerst de hele lijst met classes moet laden en op elk de _translate-methode moet aanroepen. De wachttijd vermindert in principe natuurlijk niet, maar wordt uitgesmeerd over de tijd dat de applicatie draait. De perceptie van het wachten verandert voor de gebruiker en dat is volgens mij al winst.

De eerdere suggestie om met een static initializer iets te doen, zou misschien ook kunnen werken, maar (om het even simpel te zeggen) dan zou in dat blok dus de aanroep naar zijn eigen static methode moeten staan. Helemaal goed, maar dan moet dat Translator object nog ergens vandaan worden getoverd. Ik stel me voor dat mijn eigen classloader gewoon een object referentie daarnaartoe heeft en die dus als argument zou kunnen meegeven, terwijl de static initializer 'm zelf ergens vandaan moet opduikelen. Nog steeds mogelijk trouwens, maar ik zoek een iets elegantere oplossing. Ik sluit 'm nog steeds niet uit overigens... ;)

NB: Wat betreft de voorgestelde oplossingen waarvan ik zou zeggen dat ik er naar zou gaan kijken, ga ik zeker nog bekijken. Komende dagen is het even wat rustiger waardoor ik even lekker kan researchen.

:Y)

Verwijderd

Ondanks dat ik denk dat dit niet de beste oplossing is, zo kun je een classloader er tussen zetten:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HelloClassLoaderExample {

    private static final class CustomLoader extends ClassLoader {
        
        @Override
        protected synchronized Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException {
            System.out.println("Laden van: " + name);
            return super.loadClass(name, resolve);
        }
    }

    public static void main(String[] args) throws InstantiationException,
            IllegalAccessException, ClassNotFoundException {
        ClassLoader loader = new CustomLoader();
        loader.loadClass(HelloClassLoaderExample.class.getName()).newInstance();
    }
}
Dit werkt vanaf Java 1.0 dus zelfs op Java 1.3 (weten jullie dat deze versie al een tijdje end of life is?).

Kijk eens naar de standaard faciliteiten voor vertalingen: tutorial. Dat lost ook je problemen op en levert waarschijnlijk een hoop andere nuttige features. De snelste en makkelijkste oplossing is denk ik gewoon de static initializers. Dat is één dagje copy pasten, je bent er nu al langer mee bezig. Bovendien zal een custom classloader die via reflection/lijstje _alle_ classes inspecteert ook een performance hit opleveren.
Pagina: 1