[Java]Met behulp van reflectie dynamisch classes laden.

Pagina: 1
Acties:

  • The-MeLLeR
  • Registratie: Juni 2004
  • Laatst online: 27-02 11:20
Ik ben bezig om een modulair MVC-framework te bouwen in java.

Hierbij heb ik een interface gemaakt die module heet (zodat ik alle modules hierheeb jab typecasten).
Ook heb ik een klasse gemaakt om een directory waar de modules zich in bevinden uit te lezen en een instantie te maken van elke klasse met behulp van reflection (java.lang.reflect).

Dit is natuurlijk aardig maar als ik me servlet in een war-file 'verpak' werkt het niet meer omdat ik dan met een package te maken heb i.p.v. een directory.

Ik weet dat er een object Package in java zit (java.lang.Package) maar voor zover ik weet kan ik niet met behulp van dat object een lijst (array) met klasses of klassenamen krijgen.

Heeft iemand een idee hoe dit wel kan?

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Ik denk niet dat je een instantie wilt maken van alle classes, maar alleen van de classes die niet door anderen aangemaakt worden.

Als je van deze classes een instantie kunt maken, kunnen zij indien nodig andere instanties aanmaken. Van alle classes een instantie maken is dus niet zinvol.

Verder is het gebruikelijk om een class ref op te geven mbv een String en dat op de volgende manier een instantie ervan te maken.

String fooClassStr = "com.bla.Foo";
Class fooClass = Class.forName(fooClassStr);
Foo foo = (Foo) fooClass.newInstance()

(uit mijn hoofd)

[ Voor 23% gewijzigd door Alarmnummer op 01-02-2005 21:42 ]


Verwijderd

een custom class loader maken bedoel je?

[ Voor 6% gewijzigd door Verwijderd op 01-02-2005 21:48 ]


  • The-MeLLeR
  • Registratie: Juni 2004
  • Laatst online: 27-02 11:20
Dat weet ik.
Het dynamisch laden van klasses (en het initaliseren van deze klasses) lukt ook, maar alleen zolang ze in een directory staan. Op het moment dat ik er een war (een jar maar dan voor tomcat) van maak werkt het niet meer. (omdat ik het nu doe door de map uit te lezen)...

Hoe moet dit dus voor een package?

Ter veduideliking hier me klasse om de modules te laden:
Hierbij is ClassFilter een klasse die java.io.FilenameFilter implementeerd (om alleen de .class bestanden te krijgen)

Java:
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
package com.meller.stones.util;

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

public class ModulesList {

    /**
     *  Gets the list with modules
     *
     * @param  dirname  The directory where to look for modules
     * @param  servlet  The controller servlet
     * @return          The vector with instances of modules
     */
    public static Vector getList(String dirname, javax.servlet.Servlet servlet) {
        File file = new File(dirname);
        ClassFilter classFilter = new ClassFilter();
        String fileNames[] = file.list(classFilter);
        Vector modules = new Vector();

        for (int i = 0; i < fileNames.length; i++) {
            try {
                String modulename = "Stones.Modules." + fileNames[i].substring(0, fileNames[i].length() - 6);
                Class moduleClass = Class.forName(modulename);
                Class[] parameterTypes = new Class[]{};
                try {
                    Constructor con = moduleClass.getConstructor(parameterTypes);
                    Object[] args = new Object[]{};
                    try {
                        modules.add((Module) con.newInstance(args));
                    } catch (Exception e) {
                        e.printStackTrace();
                        System.out.println("Error in constructor --> FATAL ERROR");
                    }
                } catch (NoSuchMethodException e) {
                    System.out.println("Error in constructor --> Trying with servlet argument");
                    parameterTypes = new Class[]{Class.forName("javax.servlet.http.HttpServlet")};
                    try {
                        Constructor con = moduleClass.getConstructor(parameterTypes);
                        Object[] args = new Object[]{servlet};
                        modules.add((Module) con.newInstance(args));
                    } catch (Exception e2) {
                        System.out.println("Error in constructor --> FATAL ERROR");
                        e2.printStackTrace();
                    }
                }
            } catch (ClassNotFoundException e) {
                System.out.println("CLASS NOT FOUND!");
                e.printStackTrace();
            }
        }
        return modules;
    }
}

[ Voor 4% gewijzigd door The-MeLLeR op 01-02-2005 21:51 ]


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Je moet wel . gebruiken ipv /

dus java.lang.String
ipv
java/lang/String

En nogmaals.. je wilt niet automatisch alle classes in een package gaan inladen. Vaak zitten er classes in die alleen voor de support zijn.

[ Voor 49% gewijzigd door Alarmnummer op 01-02-2005 21:53 ]


Verwijderd

de simpele manier zou zijn je war runtime te unwjarren en dan in die directoryboom te gaan zoeken.

hier staat en verhaaltje over classloading met jars, en een war is een jar met een w, das het enige verschil (en d'r zitten wat extra standaard directorietjes in een war...)
d't zitten wel wat haken en ogen aan waar je goed erg in moet hebben, i.v.m. het security mechanisme van classloading.

[ Voor 65% gewijzigd door Verwijderd op 01-02-2005 22:02 ]


  • The-MeLLeR
  • Registratie: Juni 2004
  • Laatst online: 27-02 11:20
Alarmnummer schreef op dinsdag 01 februari 2005 @ 21:52:
Je moet wel . gebruiken ipv /

dus java.lang.String
ipv
java/lang/String
Wat bedoel je hiermee? Het zijn toch gewoon punten?

En ik wil de package com.meller.stones.modules.* gaan inladen niet de complete package. In die package komen dan alle modules.

Verwijderd

van wat ik begrepen heb werkt onderstaand verhaal.

stop de package com.meller.stones.modules in z'n eigen modules.jar
doe die in je war root dir
en zet in je META-INF/MANIFEST.MF
code:
1
2
Manifest-Version: 1.0
Class-Path: modules.jar

(als je hem niet in je root dir wil, moet je het relatieve pad vanaf de war root voor modules.jar zetten)

en de jar kan je gewoon als een directory behandelen als je hem wilt uitlezen

als in
Java:
1
new File("/pad/naar/modules.jar").listFiles()
werkt zoals je zou willen
en dan kan je een class laden met
Java:
1
Class.forName("com.meller.stones.modules.[modulenaam]")

tis een omweg, maar je moet iets omdat java.lang.Package niet iets heeft als Package.listClasses() zoals je al zei

dit hele verhaal gaat er vanuit dat je vanuit de applicatie in je war de classes wilt laden

[ Voor 21% gewijzigd door Verwijderd op 01-02-2005 23:05 ]


  • Nick_S
  • Registratie: Juni 2003
  • Laatst online: 10-05 16:41

Nick_S

++?????++ Out of Cheese Error

Ik heb pas voor m'n werk eens zitten speuren in de JBoss code vanwege autodeployment van 'modules'. JBoss werkt per module met een eigen classloader. De parent van deze classloader is de classloader van jboss. (In het echt zitten er nog wat meer in, qua subdeployments etc, maar het gaat even om het idee).

In de module classloaders worden dus de jar files (jar, war, sar, tis allemaal hetzelfde, zoals hierboven al werd gezegd) geladen. En deze classloaders worden gebruikt voor het laden van de classes. Dus zelfs als twee modules van een bepaalde class een conflicterende versie hebben, gaat er nog niks fout. (Zolang je deze modules geen instanties laat uitwisselen)

Ik denk dus, dat je inderdaad wil gaan kijken naar classloaders en niet naar reflectie. Reflectie is meer bedoeld om classes uit te lezen die zelfs per aanroep kunnen verschillen (Bijvoorbeeld door een parameter aan die methode mee te geven.) Classes in Classloaders veranderen tijdens het draaien van een programma (of deel van het programma, nl. een module) niet.

Als je tijdens het draaien van een module toch andere versies van classes wil, deploy je meestal opnieuw de module.

Hmm, tis misschien een beetje een rant geworden, maar misschien heb je wat aan de informatie die ik probeer over te dragen. Al zijn het alleen maar meer vragen, waar iemand antwoord op kan proberen te geven.

'Nae King! Nae quin! Nae Laird! Nae master! We willna' be fooled agin!'

Pagina: 1