Accesso indipendente dalla posizione alle risorse
- Panoramica
- Risorse, nomi e contesti
- Risorse di sistema
- Risorse non di sistema
- Nomi delle risorse
- Usare metodi di java.lang.Class
- Using Methods ofjava.lang.ClassLoader
- Security
- Examples
Overview
Una risorsa è un dato (immagini, audio, testo e così via) a cui un programma deve accedere in un modo che è indipendente dalla posizione del codice del programma. I programmi Java possono usare due meccanismi per accedere alle risorse: Le applet usano Applet.getCodeBase()
per ottenere l’URL di base per il codice dell’applet e poi estendono l’URL di base con un percorso relativo per caricare la risorsa desiderata, per esempio con Applet.getAudioClip(url)
. Le applicazioni usano “percorsi ben noti” comeSystem.getProperty("user.home")
oSystem.getProperty("java.home")
, poi aggiungono “/lib/resource”, e aprono quel file.
I metodi nelle classi Class
eClassLoader
forniscono un modo indipendente dal percorso per localizzare le risorse. Per esempio, permettono di localizzare risorse per:
- Un’applet caricata da Internet usando connessioni HTTP multiple.
- Un’applet caricata usando file JAR.
- Un Java Bean caricato o installato nella CLASSPATH.
- Una “libreria” installata nella CLASSPATH.
Questi metodi non forniscono supporto specifico per localizzare risorse localizzate. Le risorse localizzate sono supportate dalle strutture di internazionalizzazione.
Risorse, nomi e contesti
Una risorsa è identificata da una stringa composta da una sequenza di sottostringhe, delimitata da slash (/), seguita da un nome di risorsa.Ogni sottostringa deve essere un identificatore Java valido. Il nome della risorsa ha la forma shortName
o shortName.extension
. SiashortName
che extension
devono essere identificatori Java.
Il nome di una risorsa è indipendente dall’implementazione Java; in particolare, il separatore di percorso è sempre una barra (/). Tuttavia, l’implementazione Java controlla i dettagli di come il contenuto della risorsa viene mappato in un file, database o altro oggetto contenente la risorsa effettiva.
L’interpretazione di un nome di risorsa è relativa a un’istanza del classloader. I metodi implementati dalla classeClassLoader
fanno questa interpretazione.
Risorse di sistema
Una risorsa di sistema è una risorsa che è o incorporata nel sistema, o tenuta dall’implementazione dell’host, per esempio, in un sistema di file locale. I programmi accedono alle risorse di sistema attraverso i metodiClassLoader
getSystemResource
egetSystemResourceAsStream
.
Per esempio, in una particolare implementazione, individuare una risorsa di sistema può comportare una ricerca nelle voci del CLASSPATH. I metodiClassLoader
cercano ogni directory, file ZIP o JAR nella CLASSPATH per il file della risorsa e, se trovati, restituiscono un InputStream
o il nome della risorsa. Se non viene trovato, i metodi restituiscono null. Una risorsa può essere trovata in una voce diversa della CLASSPATH rispetto alla posizione in cui il file di classe è stato caricato.
Risorse non di sistema
L’implementazione di getResource
su una classe caricata dipende dai dettagli della classe ClassLoader
. Per esempio, AppletClassLoader
:
- Prima cerca di localizzare la risorsa come risorsa di sistema; poi, se non la trova,
- Cerca tra le risorse in archivi (file JAR) già caricati in questo CODEBASE; poi, se non la trova,
- Usa CODEBASE e cerca di localizzare la risorsa (il che può comportare il contatto con un sito remoto).
Tutti i caricatori di classi cercheranno una risorsa prima come risorsa di sistema, in modo analogo alla ricerca dei file di classe. Questa regola di ricerca permette di sovrascrivere localmente qualsiasi risorsa. I client dovrebbero scegliere un nome di risorsa che sia unico (usando il nome della società o del pacchetto come prefisso, per esempio).
Nomi delle risorse
Una convenzione comune per il nome di una risorsa usata da una classe è di usare il nome pienamente qualificato del pacchetto della classe, ma convertire tutti i punti (.) in slash (/), e aggiungere un nome di risorsa della forma name.extension
. Per supportare questo, e per semplificare la gestione dei dettagli delle classi di sistema (per le qualigetClassLoader
restituisce null), la classeClass
fornisce due metodi di convenienza che chiamano i metodi appropriati in ClassLoader
.
Il nome della risorsa dato a un metodo Class
può avere una “/” iniziale che lo identifica come un nome “assoluto”.I nomi delle risorse che non iniziano con una “/” sono “relativi”.
I nomi assoluti sono spogliati della loro “/” iniziale e sono passati, senza ulteriori modifiche, al metodoClassLoader
appropriato per localizzare la risorsa. I nomi relativi sono modificati secondo la convenzione descritta in precedenza e poi sono passati a un metodo ClassLoader
.
Usare i metodi di java.lang.Class
La classe Class
implementa diversi metodi per caricare le risorse.
Il metodo getResource()
restituisce un URL per la risorsa. L’URL (e la sua rappresentazione) è specifica dell’implementazione e della JVM (cioè, l’URL ottenuto in un’istanza runtime potrebbe non funzionare in un’altra). Il suo protocollo è solitamente specifico per il ClassLoader
carico della risorsa. Se la risorsa non esiste o non è visibile per motivi di sicurezza, i metodi restituiscono null.
Se il codice client vuole leggere il contenuto della risorsa come un InputStream
, può applicare il metodoopenStream()
sull’URL. Questo è abbastanza comune da giustificare l’aggiunta di getResourceAsStream()
aClass
e ClassLoader
.getResourceAsStream()
è lo stesso che chiamaregetResource().openStream()
, eccetto chegetResourceAsStream()
cattura le eccezioni IO e restituisce un InputStream
nullo.
Il codice client può anche richiedere il contenuto della risorsa come un oggetto applicando il metodo java.net.URL.getContent()
all’URL. Questo è utile quando la risorsa contiene i dati di un’immagine, per esempio. Nel caso di un’immagine, il risultato è un oggetto awt.image.ImageProducer
, non un oggettoImage
.
I metodi getResource
egetResourceAsStream
trovano una risorsa con un nome dato. Restituiscono null se non trovano una risorsa con il nome specificato. Le regole per la ricerca delle risorse associate ad una data classe sono implementate dalClassLoader della classe. I metodi Class
delegano ai metodiClassLoader
, dopo aver applicato una convenzione di denominazione: se il nome della risorsa inizia con “/”, viene usato così com’è. Altrimenti, viene aggiunto il nome del pacchetto, dopo aver convertito tutti i punti (.) in slash (/).
public InputStream getResourceAsStream(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResourceAsStream(name); // A system class. } return cl.getResourceAsStream(name);}public java.net.URL getResource(String name) { name = resolveName(name); ClassLoader cl = getClassLoader(); if (cl==null) { return ClassLoader.getSystemResource(name); // A system class. } return cl.getResource(name);}
Il metodo resolveName
aggiunge un prefisso al nome del pacchetto se il nome non è assoluto, e rimuove qualsiasi “/” iniziale se il nome è assoluto. È possibile, anche se non comune, avere classi in pacchetti diversi che condividono la stessa risorsa.
private String resolveName(String name) { if (name == null) { return name; } if (!name.startsWith("/")) { Class c = this; while (c.isArray()) { c = c.getComponentType(); } String baseName = c.getName(); int index = baseName.lastIndexOf('.'); if (index != -1) { name = baseName.substring(0, index).replace('.', '/') + "/" + name; } } else { name = name.substring(1); } return name;}
Usare i metodi di java.lang.ClassLoader
La classe ClassLoader
ha due serie di metodi per accedere alle risorse. Una serie restituisce un InputStream
per la risorsa. L’altro set restituisce un URL. I metodi che restituiscono un InputStream
sono più facili da usare e soddisferanno molte esigenze, mentre i metodi che restituiscono URL forniscono accesso a informazioni più complesse, come un’immagine e un AudioClip.
La classe ClassLoader
modifica le risorse in modo simile a come gestisce le classi. Un ClassLoader
controlla come associare il nome di una risorsa al suo contenuto. ClassLoader
fornisce anche metodi per accedere alle risorse di sistema, analogamente alle classi di sistema. La classe Class
fornisce alcuni metodi di convenienza che delegano la funzionalità ai metodi ClassLoader
.
Molti programmi Java accederanno a questi metodi indirettamente attraverso le API I18N (localizzazione). Altri vi accederanno attraverso i metodi in Class
. Alcuni invocheranno direttamente i metodi ClassLoader
.
I metodi in ClassLoader
usano la stringa data come nome della risorsa senza applicare alcuna trasformazione assoluta/relativa (vedere i metodi in Class). Il nome non dovrebbe avere un “/” iniziale.
Le risorse di sistema sono quelle che sono gestite direttamente dall’hostimplemenation. Per esempio, possono trovarsi nelCLASSPATH.
Il nome di una risorsa è una sequenza di identificatori separati da “/”. La classe Class
fornisce metodi di convenienza per accedere alle risorse; i metodi implementano una convenzione in cui il nome del pacchetto è preceduto dal nome breve della risorsa.
Si può accedere alle risorse come un InputStream
, o unaURL.
Il metodo getSystemResourceAsStream
restituisce unInputStream per la risorsa di sistema specificata o null se non trova la risorsa. Il nome della risorsa può essere qualsiasi risorsa di sistema.
Il metodo getSystemResource
trova una risorsa di sistema con il nome specificato. Restituisce un URL alla risorsa o null se non trova la risorsa. Chiamandojava.net.URL.getContent()
con l’URL restituirà un oggetto come ImageProducer
, AudioClip
o InputStream
.
Il metodo getResourceAsStream
restituisce unInputStream
per la risorsa specificata o nullo se non trova la risorsa.
Il metodo getResource
trova una risorsa con il nome specificato. Restituisce un URL alla risorsa o null se non trova la risorsa. Chiamandojava.net.URL.getContent()
con l’URL restituirà un oggetto come ImageProducer
, AudioClip
o InputStream
.
Sicurezza
Poiché getResource()
fornisce accesso alle informazioni, deve avere regole di sicurezza ben definite e fondate. Se le considerazioni sulla sicurezza non permettono ad una risorsa di essere visibile in qualche contesto di sicurezza, il metodo getResource()
fallirà (restituirà null) come se la risorsa non fosse affatto presente, questo affronta gli attacchi all’esistenza.
I caricatori di classe non possono fornire accesso al contenuto di un file .class per ragioni sia di sicurezza che di performance. Se è possibile ottenere un URL per un file .class dipende dalle specifiche, come mostrato di seguito.
Non ci sono problemi di sicurezza specifici o restrizioni riguardanti le risorse che vengono trovate da un caricatore di classi non di sistema.AppletClassLoader
fornisce l’accesso alle informazioni che sono caricate da una posizione sorgente, sia individualmente, sia in gruppo attraverso un file JAR; quindi AppletClassLoader
dovrebbe applicare le stesse regole checkConnect()
quando si tratta di URL attraverso getResource()
.
Il sistema ClassLoader
fornisce accesso alle informazioni nella CLASSPATH. Una CLASSPATH può contenere directory e file JAR. Poiché un file JAR è creato intenzionalmente, ha un significato diverso da una directory dove le cose possono finire in modo più casuale. In particolare, siamo più rigorosi nell’ottenere informazioni da una directory che da un file JAR.
Se una risorsa è in una directory:
-
getResource()
le invocazioni userannoFile.exists()
per determinare se rendere il file corrispondente visibile all’utente. Ricordate cheFile.exists()
usa il metodocheckRead()
nel security manager. - lo stesso vale per
getResourceAsStream()
.
Se la risorsa è in un file JAR:
-
getResource()
le invocazioni avranno successo per tutti i file, indipendentemente dal fatto che l’invocazione sia fatta da un sistema o da una classe non sistema. -
getResourceAsStream()
le invocazioni avranno successo per risorse non di classe, e così perjava.net.URL.getContent()
sugli URL corrispondenti.
Esempi
Questa sezione fornisce due esempi di codice client. Il primo esempio usa nomi di “risorse assolute” e meccanismi tradizionali per ottenere un oggetto Class
.
package pkg;import java.io.IOException;import java.io.InputStream;import java.io.PrintStream;class Test { private static final String absName = "/pkg/mumble.baf"; public static void test1() { Class c=null; try { c = Class.forName("pkg.Test"); } catch (Exception ex) { // This should not happen. } InputStream s = c.getResourceAsStream(absName); // do something with it. } public void test2() { InputStream s = this.getClass().getResourceAsStream(absName); // do something with it. }}
Questo esempio usa nomi di “risorse relative” e il meccanismo disponibile dal compilatore attraverso il flag -experimental
, per ottenere un oggetto Class
.
package pkg;import java.io.IOException;import java.io.InputStream;import java.io.PrintStream;class Test { private static final String relName = "mumble.baf"; public static void test1() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it.} public void test2() { InputStream s = Test.class.getResourceAsStream(relName); // do something with it. }