Suggerimento Java 105: padroneggiare il classpath con JWhich

Prima o poi, gli sviluppatori provano frustrazione quando hanno a che fare con il percorso di classe Java. Non è sempre chiaro quale classe verrà caricata dal programma di caricamento classi, specialmente quando il percorso di classe dell'applicazione viene inondato di directory e file. In questo articolo, presenterò uno strumento in grado di visualizzare il percorso assoluto del file di classe caricato.

Nozioni di base su Classpath

La Java virtual machine (JVM) utilizza un programma di caricamento classi per caricare le classi utilizzate da un'applicazione in base alle necessità. La CLASSPATHvariabile di ambiente indica al programma di caricamento classi dove trovare le classi di terze parti e definite dall'utente. È inoltre possibile specificare il percorso di classe in base all'applicazione con l' -classpathargomento della riga di comando JVM, che sostituisce il percorso di classe specificato nella CLASSPATHvariabile di ambiente.

Le voci del percorso di classe possono essere directory che contengono file di classe per classi non in un pacchetto, directory radice del pacchetto per classi in un pacchetto o file di archivio (come file .zip o .jar) che contengono classi. Le voci del percorso di classe sono separate da due punti sui sistemi di tipo Unix e da un punto e virgola sui sistemi MS Windows.

I programmi di caricamento classi sono organizzati in una gerarchia di delega, con ogni programma di caricamento classi dotato di un programma di caricamento classi padre. Quando a un programma di caricamento classi viene chiesto di trovare una classe, per prima cosa delega la richiesta al programma di caricamento classi padre prima di tentare di trovare la classe stessa. Il programma di caricamento classi di sistema, il programma di caricamento classi predefinito fornito da JDK o JRE installato sul sistema, carica le classi di terze parti e definite dall'utente utilizzando la CLASSPATHvariabile di ambiente o l' -classpathargomento della riga di comando JVM. Il programma di caricamento classi di sistema delega alla classe di estensione il caricamento delle classi che utilizzano il meccanismo di estensione Java. Il programma di caricamento classi di estensione delega al programma di caricamento classi bootstrap (il buck si ferma qui!) Per caricare le classi JDK principali.

È possibile sviluppare programmi di caricamento classi specializzati per personalizzare il modo in cui la JVM carica dinamicamente le classi. Ad esempio, la maggior parte dei motori servlet utilizza un programma di caricamento classi personalizzato per ricaricare dinamicamente le classi servlet che sono state modificate nelle directory specificate in un percorso classi personalizzato.

Di particolare importanza e con molta costernazione, il programma di caricamento classi caricherà le classi nell'ordine in cui appaiono nel classpath. A partire dalla prima voce del percorso di classe, il caricatore di classi visita ogni directory o file di archivio specificato cercando di trovare la classe da caricare. La prima classe che trova con il nome corretto viene caricata e tutte le voci rimanenti del percorso di classe vengono ignorate.

Sembra semplice, vero?

Inganno del percorso di classe

Che lo ammettano o no, gli sviluppatori Java principianti e veterani allo stesso modo ad un certo punto (di solito nel momento peggiore possibile!) Sono stati ingannati dal percorso di classe oneroso. Poiché il numero di classi dipendenti di terze parti e definite dall'utente aumenta per un'applicazione e il classpath diventa una discarica per ogni directory immaginabile e file di archivio, non è sempre ovvio quale classe verrà caricata per prima dal programma di caricamento classi. Ciò è particolarmente vero nel malaugurato caso in cui il classpath contenga voci di classe duplicate. Ricorda, il programma di caricamento classi carica la prima classe con un nome appropriato che trova nel percorso di classe e "nasconde" di fatto tutte le altre classi con precedenza inferiore denominate correttamente.

È fin troppo facile cadere vittima di questo inganno dei percorsi di classe. Dopo una lunga giornata di schiavitù su una tastiera calda, aggiungi una directory al classpath nel tentativo di ottenere l'ultima e migliore versione di una classe caricata nell'applicazione, senza sapere che un'altra versione della classe si trova in una directory di precedenza più alta nel classpath. Gotcha!

JWhich: un semplice strumento classpath

Il problema di precedenza inerente a una dichiarazione di percorso semplice non è univoco per il percorso di classe Java. Per trovare una soluzione al problema è sufficiente stare sulle spalle di leggendari giganti del software. Il whichcomando del sistema operativo Unix prende un nome e visualizza il percorso del file che verrebbe eseguito se il nome fosse stato emesso come comando. In sostanza attraversa la PATHvariabile di ambiente per individuare la prima occorrenza del comando. Sembra anche uno strumento potente per la gestione del classpath Java. Ispirato da questa nozione, ho iniziato a scrivere un'utilità Java che potesse prendere un nome di classe Java e visualizzare il percorso assoluto del file di classe che il programma di caricamento classi avrebbe caricato, come prescritto dal classpath.

Il seguente esempio di utilizzo di JWhichvisualizza il percorso assoluto della prima occorrenza della com.clarkware.ejb.ShoppingCartBeanclasse che deve essere caricata dal programma di caricamento classi, che si trova in una directory:

 > java JWhich com.clarkware.ejb.ShoppingCartBean Class 'com.clarkware.ejb.ShoppingCartBean' trovato in '/home/mclark/classes/com/clarkware/ejb/ShoppingCartBean.class' 

Il seguente esempio di utilizzo di JWhichvisualizza il percorso assoluto della prima occorrenza della javax.servlet.http.HttpServletclasse che deve essere caricata dal caricatore di classi, che risulta essere impacchettato in un file di archivio:

 > java J Che javax.servlet.http.HttpServlet Class 'javax.servlet.http.HttpServlet' trovato nel 'file: /home/mclark/lib/servlet.jar! /javax/servlet/http/HttpServlet.class' 

Come funziona JWhich

Per determinare in modo inequivocabile quale classe verrà caricata per prima nel classpath, è necessario entrare nella mente del class loader. Non è così difficile come sembra: basta chiederlo! Il codice sorgente pertinente per JWhichsegue. Per il codice sorgente completo, vedere Risorse.

1: public class J Che {2: 3: / ** 4: * Stampa il percorso assoluto del file di classe 5: * contenente il nome di classe specificato, come prescritto 6: * dal classpath corrente. 7: * 8: * @param className Nome della classe. 9: * / 10: public static void which (String className) {11:12: if (! ClassName.startsWith ("/")) {13: className = "/" + className; 14:} 15: className = className.replace ('.', '/'); 16: className = className + ".class"; 17:18: java.net.URL classUrl = 19: new JWhich (). GetClass (). GetResource (className); 20:21: if (classUrl! = Null) {22: System.out.println ("\ nClass '" + className + 23: "' trovato in \ n '" + classUrl.getFile () + "'"); 24:} else {25: System.out.println ("\ nClass '" + className + 26: "' non trovato in \ n '"+ 27: System.getProperty (" java.class.path ") +" '"); 28:} 29:} 30: 31: public static void main (String args []) {32: if (args.length > 0) {33: JWhich.which (args [0]); 34:} else {35: System.err.println ("Usage: java JWhich"); 36:} 37:} 38:}

First, you need to massage the class name a bit to gain class loader acceptance (lines 12-16). Prepending a "/" to the class name instructs the class loader to match the class name verbatim within the classpath, rather than trying to implicitly prepend the package name of the invoking class. Converting each occurrence of "." to "/" formats the class name as a valid URL resource name required by the class loader.

Next, the class loader is interrogated (lines 18-19) for the resource matching the properly formatted class name. Every Class object maintains a reference to the ClassLoader object that loaded it, so the class loader that loaded the JWhich class itself is interrogated here. The Class.getResource() method actually delegates to the class loader that loaded the class, returning a URL for reading the class file resource, or null if a class file resource with the specified class name could not be found in the current classpath.

Finally, the absolute pathname of the class file containing the specified class name is displayed, if it was found in the current classpath (lines 21-24). As a debugging aid, if the class file was not found in the current classpath, you obtain the value of the java.class.path system property to display the current classpath (lines 24-28).

It's easy to imagine how this simple chunk of code could be invoked in a Java servlet using the servlet engine's classpath or an Enterprise JavaBean (EJB) using the EJB server's classpath. If the JWhich class were loaded by the custom class loader in a servlet engine, for example, then the servlet engine's class loader would be used to find classes. If the servlet engine's class loader is unable to locate a class, it will delegate to its parent class loader. In general, when JWhich is loaded by a class loader, it's able to find all classes loaded by its class loader or any parent class loaders.

Conclusion

Se la necessità è la madre di tutte le invenzioni, uno strumento che aiuti a gestire il classpath Java è atteso da tempo. I newsgroup e le mailing list relativi a Java sono pieni zeppi di domande relative al classpath. Dobbiamo abbassare la barriera all'ingresso di nuovi sviluppatori in modo da poter continuare a lavorare a livelli più elevati di astrazione. JWhichè uno strumento semplice, ma potente, che ti aiuterà a padroneggiare il percorso di classe Java in qualsiasi ambiente.

Mike Clark è un consulente indipendente per Clarkware Consulting, specializzato in architettura, progettazione e sviluppo basati su Java utilizzando tecnologie J2EE. Ha recentemente completato lo sviluppo e l'implementazione di un server di scambio XML business-to-business (B2B) ed è attualmente consulente per un progetto che costruisce un prodotto di gestione delle prestazioni J2EE.

Ulteriori informazioni su questo argomento

  • Obtain the full source code for this article

    //images.techhive.com/downloads/idge/imported/article/jvw/2000/12/jwhich.zip

  • A full-featured version of JWhich, including a classpath validator, is available at

    //www.clarkware.com/software/jwhich.zip

  • Official documentation for the Sun JDK and how it deals with the classpath for the various officially supported platforms is available at

    //java.sun.com/j2se/1.3/docs/tooldocs/findingclasses.html

  • For details on how to set the classpath on Unix and Windows platforms, see "Setting the classpath" at:
  • Unix

    //java.sun.com/j2se/1.3/docs/tooldocs/solaris/classpath.html

  • Windows

    //java.sun.com/j2se/1.3/docs/tooldocs/win32/classpath.html

  • View all previous Java Tips and submit your own

    //www.javaworld.com/javatips/jw-javatips.index.html

  • Per ulteriori trucchi Java, iscriviti al di ITworld.com libero Java Tutor newsletter

    //www.itworld.com/cgi-bin/subcontent12.cgi

  • Parla nella discussione su Java Beginner, moderata dall'autore di JavaWorld Geoff Friesen

    //www.itworld.com/jump/jw-javatip105/forums.itworld.com/[email protected]@.ee6b804/1195!skip=1125

Questa storia, "Java Tip 105: Mastering the classpath with JWhich" è stata originariamente pubblicata da JavaWorld.