Newsgroup: it.comp.java
Titolo: Problema urgente con la classe Method!!!

Ciao a tutti, è da un po' di mesi che sto realizzando un server web in Java 
che supporti le servlet. Sono arrivato ad un punto in cui riesco a caricare 
le classi a run-time, ma non riesco a richiamarne i metodi a cui passo delle 
classi create da me. Mi spiego meglio: questo la parte di codice che non 
funziona:
        Object objectLoaded = classLoaded.newInstance();
        Method method = classLoaded.getMethod("init");
        method.invoke(objectLoaded);
        // Fin qui tutto funziona
        // ServletConfig è una classe creata da me
        Class[] argsType = {ServletConfig.class};
        /* Qui il metodo "getMethod" solleva una eccezione 
NoSuchMethodException dicendo che non esiste un metodo init a cui passo come 
parametro una classe ServletConfig, ma ho controllato bene: questo metodo 
esiste! Infatti se provo a fare "method.getParameterTypes()" questo mi 
ritorna come classe "ServletConfig". Cosa però strana: quando visualizzo a 
video il nome di questa classe mi da appunto "ServletConfig", ma se provo a 
fare un confronto tra questa classe e "ServletConfig.class", mi ritorna 
false, è normale? */
        method = classLoaded.getMethod("init", argsType);
        Object argsArray[] = {new ServletConfig()};
        method.invoke(objectLoaded, argsArray);
Spero di essere stato chiaro: se non capite qualcosa, fatemelo sapere! 
Grazie!
Distinti saluti
Matteo 



Innanzi tutto complimenti per il lavoro!!!
Secondo me il problema è alla riga
         Object argsArray[] = {new ServletConfig()};
In realtà se guardi la documentazione delle Servlet API (che puoi trovare
all'indirizzo 
http://java.sun.com/j2ee/1.4/docs/api/index.html
 ) nel package
javax.servlet, noterai che ServletConfig è un'interfaccia. Quello che
dovresti fare te in questo caso è crearti una classe che implementi
javax.servlet.ServletConfig e passarla come argomento del metodo init della
servlet. Tieni sempre presente che NON DEVI utilizzare lo stesso nome
dell'interfaccia. Piuttosto dichiarala in questo modo:
---------- file MyWebServletConfig.java ----------
package <il tuo package>;
import java.util.*;
import javax.servlet.*;
public class MyWebServletConfig implements javax.servlet.ServletConfig {
   String getInitParameter(String name) {
      //implementazione
   }
 
   Enumeration getInitParameterNames() {
      //implementazione
   }
   ServletContext getServletContext() {
      //implementazione
   }
   String getServletName() {
      //implementazione
   }
}
---------- fine file ----------
A questo punto potresti riscrivere l'inizializzazione in questo modo:
   ...
   Object objectLoaded = classLoaded.newInstance();
   Method method = classLoaded.getMethod("init");
   method.invoke(objectLoaded);
   Class[] argsType = {javax.servlet.ServletConfig.class};
   method = classLoaded.getMethod("init", argsType);
   Object argsArray[] = {new MyWebServletConfig()};
   method.invoke(objectLoaded, argsArray);
   ...
 
In questo modo dovrebbe funzionare.
Spero possa esserti utile
-- 
Fermiamo scientology prima che sia troppo tardi!!!
http://xenu.com-it.net
 - 
http://xenu.freewinds.cx/
 - 
http://www.xenu.net/
(per rispondere personalmente leva IlDitoDalCulo)


Anzitutto ti ringrazio per avermi risposto!!!
Tutto ciò che hai detto va bene, il problema è che non posso usare classi 
già fatte come javax.servlet.ServletConfig! Provando e riprovando ora sono 
arrivato a questo punto:
        Method[] prova = classLoaded.getDeclaredMethods();
        Class[] argsType = prova[0].getParameterTypes();  // Faccio questa 
operazione sapendo che il primo metodo è quello che mi interessa!
        System.out.println(argsType[0].getSimpleName());   // Qui, infatti, 
mi visualizza la classe passata come parametro, ovvero ServletConfig
        method = classLoaded.getMethod("init", argsType);
        Object argsArray[] = {argsType};  // Questa e la successiva 
istruzione hanno qualcosa che non va, ma cosa?
        method.invoke(objectLoaded, argsArray);
Prima mi dava problemi sul "getMethod": ora con questo codice "getMethod" 
non solleva più eccezioni, ma me ne solleva una (IllegalArgumentException) 
sul metodo "invoke", dove sbaglio? Ciao e grazie!
P.S.: ho provato a fare una mia interfaccia chiamandola "ServletConfig", ho 
fatto una mia classe "MyWebServletConfig" che implementa "ServletConfig", ho 
fatto tutte le modifiche che mi hai detto, ma niente, non funziona...
Distinti saluti
Matteo 


Un'altra cosa: perchè se provo a fare eseguire questo codice mi ritorna un 
eccezione "java.lang.ClassCastException"?
Object objectLoaded = classLoaded.newInstance(); // classLoaded è la classe 
"acquisita" a run-time: sarebbe la servlet
HttpServlet prova = (HttpServlet) objectLoaded; // solleva l'eccezione, come 
mai?
Se riuscissi a risolvere questo problema, potrei usare "prova" richiamando 
tutti i metodi che voglio! Ciao!
Distinti saluti
Matteo 


Senza mollare sono arrivato fino a questo punto:
        Method[] prova = classLoaded.getDeclaredMethods();
        Class[] argsType = prova[0].getParameterTypes();
        System.out.println(argsType[0].getSimpleName());
        method = classLoaded.getMethod("init", argsType);
        Object[] argsArray = {argsType[0].newInstance()};
        method.invoke(objectLoaded, argsArray);
Il metodo init a cui passo un oggetto di tipo "ServletConfig" viene 
richiamato. C'č un piccolo problema perň: come faccio ad inizializzare 
l'oggetto di tipo "ServletConfig", dato che non posso fare questo casting?
        ServletConfig config = (ServletConfig) argsType[0].newInstance(); // 
solleva un eccezione di tipo ClassCastException
Scusate se continuo ad insistere, ma per giugno devo averlo completato! 
Grazie!
Distinti saluti
Matteo 

 HttpServlet prova = (HttpServlet) objectLoaded; // solleva l'eccezione, come mai?
Perchè "objectloaded" estende la classe javax.servlet.http.HttpServlet (ne
sono più che sicuro se hai compilato correttamente almeno la servlet),
mentre da quello che ho capito finora, potresti perfino essere arrivato a
reimplementare HttpServlet nel tuo progetto... che non è più l'originale
delle specifiche stilate da Sun Microsystems
La soluzione ai tuoi problemi alla fine è sintetizzabile in:
1) rispetta gli standard
2) non reinventare la ruota
3) LEGGI LA DOCUMENTAZIONE, soprattutto quella dei package
   javax.servlet.*
   javax.servlet.http.*
Buona fortuna.


 aspetta una qualsiasi implementazione di javax.servlet.ServletConfig (...)
Ma non è la servlet che si aspetta l'implementazione di 
javax.servlet.ServletConfig: la servlet dovrebbe aspettarsi un oggetto di 
tipo ServletConfig fatto da me!
Forse c'è stato fin dall'inizio un piccolo fraintendimento tra me e te (o 
forse stò ancora capendo male io :-): se così fosse, perdonami): quando, nei 
messaggi precedenti parlavo di "servlet" non intendevo "servlet" come una 
classe derivata da javax.servlet.http.HttpServlet! Intendevo dire "servlet" 
come una classe che ne deriva da un'altra fatta DA ME che si chiama, 
appunto, HttpServlet ma che è presente nel package webserver.script (che è 
un mio package)! Quello che stò cercando di fare, è quello di "ricreare" da 
zero il "meccanismo" (cioè classi, interfacce e quant'altro) che sta dietro 
alle servlet vere e proprie. Infatti il risultato mio non sarà MAI 
compatibile al 100% con le servlet che noi conosciamo, anzi... se riuscissi 
ad arrivare al 30-40% sarebbe un buon risultato (implementando soltanto 
metodi e oggetti più comuni).
Ricapitolando brevemente, io ho una serie di classi (4 finora: HttpServlet, 
ServletConfig, HttpServletRequest e HttpServletResponse, fatte da me e che 
sono raggruppate nel package webserver.script.*!), una servlet (classe che 
estende HttpServlet, quest'ultima fatta sempre da me e presente nell'elenco 
delle 4 classi di prima) che fa l'override del metodo init (a cui passo come 
parametro l'oggetto ServletConfig, che è stato fatto da me): il problema mio 
è quello di riuscire ad invocare un metodo (in questo caso l'init) a cui 
passo un oggetto ServletConfig (fatto da me), da una servlet derivata da 
HttpServlet (fatta sempre da me): per questo non vedo il perchè java debba 
aspettarsi un oggetto di tipo javax.servlet.ServletConfig, quando invece 
dovrebbe aspettarsi un oggetto di tipo webserver.script.ServletConfig 
(quella fatta da me). Quando provo a visualizzare il tipo di classe che 
viene passato come parametro al metodo init, java mi ritorna 
"webserver.script.ServletConfig" (che è quello giusto), il problema è che 
quando richiamo i metodi "invoke" e "getMethod", ServletConfig non la 
accettano (che invece è la classe presente in webserver.script).
Spero di non aver fatto confusione (dubito...)! Sono preoccupato perchè 
questo è l'argomento della mia tesina di maturità, spero di riuscire a 
finirla in tempo!
Grazie veramente!!!
Distinti saluti
Matteo 

         Object objectLoaded = classLoaded.newInstance();




A parte le ovvie considerazioni sul senso di rifare tutto
daccapo quando ci sono gia' delle interfacce li' pronte
per essere utilizzate, ti stai complicando comunque la vita.
Mi spiego...
Se prendi spunto dalle API Sun, vedrai che l'interfaccia
javax.servlet.Servlet (da cui deriva la javax.servlet.http.HttpServlet)
dichiara il metodo init(ServletConfig).
Ora, se il tuo servlet container (ma non potremmo chiamarlo cosi', visto
che non ti basi sulle specifiche e sulle interfacce Sun...) si
aspetta di "caricare" classi che implementano la *tua* interfaccia
(si chiama miopackage.qualcosa.HttpServlet?), allora la domanda e': 
quali metodi prevede tale interfaccia? Se anch'essa dichiara un metodo
di inizializzazione del tipo init(miopackage.qualcosa.ServletConfig), 
allora ti basta fare cosi':
[...]
// Determini la classe della "servlet" da caricare
Class classToLoad = Class.forName(...);
try
{
  // Crei una istanza della "servlet"
  miopackage.qualcosa.HttpServlet theServlet = 
   (miopackage.qualcosa.HttpServlet) classToLoad.newInstance();
  // Crei (o usi) l'istanza di ServletConfig
  miopackage.qualcosa.ServletConfig config = ...;
  // Et voila'! Inizializzi senza il bisogno di ricorrere alla 
  // reflection per invocare un metodo che, per il fatto di essere 
  // dichiarato dall'interfaccia HttpServlet, DEVE essere definito 
  // da TUTTE le implementazioni CONCRETE di tale interfaccia
  theServlet.init(config);
}
catch (ClassCastException e)
{
  // Qui gestisci il caso di chi vuol fare il "furbo" e fare
  // "girare" qualcosa che non e' una "servlet"
}
[...]
Ciao!
Roberto
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it


Grazie per avermi risposto! Io, infatti, ho provato a fare una cosa simile:
WebServerClassLoader classLoader = new WebServerClassLoader(); // 
ClassLoader "fatto" da me e funzionante
Class classLoaded
try {
    classLoaded = classLoader.loadClass(...............); // Carico la 
"servlet" a run-time
} catch (NoClassDefFoundError ex) {
    // Gestione dell'eccezione
}
// Fin qui ok: la riga seguente genera eccezione "ClassCastException", come 
mai?
miopackage.HttpServlet httpServlet = (miopackage.HttpServlet) 
classLoaded.newInstance();
Sono sicuro che la "servlet" che carico a run-time derivi da 
miopackage.HttpServlet, infatti se faccio il controllo:
classLoaded.getSuperclass().getName().equalsIgnoreCase("miopackage.HttpServlet") 
 --> ritorna true!
Non è una contraddizione quella che sta facendo java? Mi dice che la classe 
deriva da miopackage.HttpServlet, ma non me la lascia castare in modo 
esplicito. Sto sbagliando il casting? Grazie!
Distinti saluti
Matteo 

Infiliset wrote:

E' praticamente certo che sia un problema del tuo codice e non di Java....
Controlla meglio l'implementazione del tuo ClassLoader.
Ciao
Roberto


Posto il codice del mio semplice ClassLoader: se hai tempo riusciresti a 
darci una "sbirciatina"? E' da giorni che ho 'sto cavolo di problema e spero 
di risolverlo il prima possibile! Se non ti disturbo troppo mi faresti un 
grosso piacere! Ciao!
package webserver;
import java.io.File;
import java.io.FileInputStream;
import webserver.script.HttpServlet;
public class WebServerClassLoader extends ClassLoader {
    private byte[] getBytes(String fileName) throws Exception {
        File file = new File(fileName);
        FileInputStream fileInputStream = new FileInputStream(file);
        long length = file.length();
        byte[] raw = new byte[(int) length];
        int bytesRead = fileInputStream.read(raw);
        if (bytesRead != length)
            throw new Exception();
        fileInputStream.close();
        return raw;
    }
    public Class loadClass(String name, boolean resolve) throws 
ClassNotFoundException {
        Class finalClass = null;
        finalClass = findLoadedClass(name);
        String fileStub = name.replace('.', '/');
        String classFileName = fileStub + ".class";
        try {
            byte[] raw = getBytes(classFileName);
            finalClass = defineClass(null, raw, 0, raw.length);
        } catch (Exception ex) {
            finalClass = null;
        }
        if (finalClass == null)
            finalClass = findSystemClass(name);
        if (resolve && finalClass != null)
            resolveClass(finalClass);
        /* Questo codice è solo di prova: ho provato qui a castare da 
HttpServlet in classe caricata, ad HttpServlet come istanza, ma nulla da 
fare, sempre la famosa eccezione ClassCastException! */
        if (finalClass.getName().equals("webserver.script.HttpServlet")) {
            try {
                HttpServlet prova = (HttpServlet) finalClass.newInstance();
            } catch (Exception ex) {
                System.out.println(ex);
            }
        }
        // Fine codice di prova!
        return finalClass;
    }
}