Indice - Progetto di Reti di Calcolatori - Fabio Adani e Marco Chiesi
Implementazione

Caricamento delle classi

L'applicazione ha un'architettura modulare, e quindi, si desidera che sia possibile estendere le funzionalità dell'applicazione, ovvero nel nostro caso inserire nuovi giochi. Chiaramente si vuole che l'inserimento di nuovi giochi non richieda la modifica del codice esistente, ma bensì il sistema deve essere in grado in modo automatico di rilevare quali moduli di gioco sono installati. Questo problema è presente sia a livello di client che di server, dato che ogni nuovo gioco richiede la creazione di un modulo client e di un modulo server. Dato che l'ambiente di sviluppo è Java è naturale pensare che ad ogni modulo corrisponda un file JAR (Java ARchive).

Si pone quindi il problema di riconoscere e soprattutto di caricare delle classi da un modulo di gioco, anche se la presenza di questo modulo potrà essere determinata soltanto a tempo di esecuzione. Per realizzare questo meccanismo si è fatto uso delle API della "reflection" di Java, contenute nel package java.lang e java.lang.reflect, ed in particolare della classe Class, che rappresenta una classe o un'interfaccia in un'applicazione Java in esecuzione. Tale classe è dotata di un metodo che consente di caricare una classe dato il suo nome:

Class class = Class.forName(String className);

Una volta che la classe è stata caricata, sarà possibile ottenere un'istanza di tale classe, con il metodo:

Object class.newInstance();

Questo però è possibile solo nel caso in cui la classe sia dotata di un costruttore senza argomenti. Altrimenti le cose sono un po' più complicate ed è necessario ricorrere ad altre classi della reflection come Constructor. Tuttavia nel nostro caso è sufficiente usare il metodo appena visto che le classi che implementano il server e il client dei giochi saranno dotati di un costruttore senza argomenti.

Perchè il caricamento della classe abbia successo, è necessario che la classe sia contenuta nel CLASSPATH della JVM attualmente in esecuzione, altrimenti la classe non verrà trovata. Visto che nel nostro caso la classe da caricare si trova all'interno di un file JAR, per ovviare a tale problema ci sono due soluzioni:

  • Si fa in modo che il file JAR sia indicato all'interno del CLASSPATH
  • Anzichè usare Class.forName(), si usa il metodo loadClass della classe URLClassLoader, la quale permette di caricare una classe a partire da un URL (nel nostro caso l'URL sarà l'indirizzo del file JAR).

Rimane ora il problema di determinare il nome della classe da caricare, cosa che deve avvenire runtime visto che a priori non è possibile conoscere i nomi dei giochi installati. Per fare questo si è adottata una regola per assegnare i nomi ai vari moduli. Questa regola consiste nel prendere il nome del gioco (ad esempio Risiko) e fare in modo che i file JAR abbiano nome formato dal nome del gioco più la parola "Client" o "Server" a seconda. Ad esempio, nel caso di Risiko, i due file si chiameranno RisikoClient.jar e RisikoServer.jar. In questo modo è possibile, a partire dal nome del file, risalire al nome del gioco e di conseguenza anche al nome della classe da caricare. Infatti, anche per i nomi delle classi e delle interfacce si sono adottate delle apposite regole di denominazione.

Pertanto l'applicazione (sia il client che il server) quando viene avviata va a scandire un'apposita cartella del file system locale ed in base alla presenza dei file JAR determina quali giochi sono installati.

Per il caricamento delle classi, è stata poi creata una classe NetgameClassLoader, avente soltanto metodi statici che consente di caricare il client o il server di un gioco dato il suo nome, con i seguenti metodi:

public static IGameServer loadServer(String gameName) throws ... ;
public static IGameClient loadClient(String gameName) throws ... ;

Indietro Inizio pagina Avanti
Indice   Fabio Adani e Marco Chiesi