Carga de Clases
El JNDI es un API definido independientemente de cualquier implementación de
nombrado específico o servico de directorio. Para que una aplicación, un
applet, o una unidad de programa puedan usar JNDI, deben especificar el
proveedor de servicios a usar y deben tener acceso a los ficheros class del
proveedor. Un sólo programa podría usar más de un proveedor. Además, el
programa y/o los proveedores podrían usar facotrías
de objetos, factorías
de clases, y factorías
de control, cuyos ficheros class también deben estar disponibles para el
JNDI. Además, el JNDI necesita acceder a los ficheros de recursos de
aplicación (ver la sección Ficheros
de Recursos de Aplicación) proporcionados por el programa, los proveedores
y otros componentes. En todos estos casos, el JNDI necesita cargar clases y
ficheros de recursos. Esta sección explica cómo el JNDI usa los cargadores de
clases y cómo nos puede afectar su utilización.
Cargadores de Clases en Segundo Plano
La carga de clases significa que las clases Java
y los recursos se cargan dentro del JRE (Java Runtime Environment). Controla un
rango de distintas políticas como desde dónde cargar las definiciones de
clases hasta el formato de los datos de las definiciones de clases.
En versiones anteriores del JDK
1.1, no existía relación entre distintos cargadores de clases, Un cargador
de clases del sistema es responable de cagar en el JRE, la aplicación y las
clases y recursos en el classpath de la aplicación. Un cargador de clases de
applet es resposnable de cargar los applets y sus recursos relacionados,
posiblemente a través de la red comunicándose con un servidor Web.
En la Java 2 Platform,
Standard Edition, v1.2 y versiones posteriores, los cargadores de clases
tienen una relación jerárquica. Cada cargador de clase tiene un cargador de
clases padre. Cuando se le pide a un cargador de clases que cargue una clase o
un recurso, consulta con su cargador de clases padre antes de intentar cargar el
ítem el mismo. El padre, a su vez consulta a su padre, etc. Por esto es sólo
si después de todos los cargadores de clases ancestros no pueden
encontrar el ítem que el cargador de clases actual obtiene.
Un cargador de clases bootstrap es el responsable de
cargar el JRE. Es la "ráiz" del árbol de cargadores de clases. El
cargador de clases del sistema es un descendiente del cargador de clases bootstrap.
Es el responsable de cargar la aplicación, así como las clases y recursos en
el classpath de la aplicación.
La plataforma Java 2 también presenta la noción de cargador de clases de
contexto. Un cargador de clases de contexto de threads esta, por defecto,
configurado como el cargador de clases de contexto de los padres del thread. La
jerarquía de threads esta enraizada en el thread primordial (el que ejecuta el
programa). El cargador de clases de contexto del thread primordial es
configurado como el cargador de clases que cargó la aplicación. Por eso amenos
que cambiemos explícitamente el cargador de clases de contexto del thread, su
cargador de clases de contexto será el mismo que el de la aplicación. Es
decir, el cargador de clases de contexto puede cargar las clases que pueda
cargar la aplicación. Este cargador lo usa el JRE como el RMI (Java Remote
Method Invocation) para cargar clases y recursos en beneficio del usuario de la
aplicación. El cargador de clases de contexto, como el cargador de la
plataforma Java 2, tiene un cargador de clases padre y soporta el mismo modelo
de delegación para la carga de clases descrito anteriormente.
Cargar Clases con el Software JDK 1.1
Cuando usamos el JNDI con el JDK 1.1, debemos situar los ficheros JARs del
proveedor de servicios y los ficheros JARs o los ficheros class que contienen
las factorías en el classpath de la aplicación. Si estamos usando un applet,
debemos situar dichos ficheros en el directorio codebase del applet y/o en las
localizaciones de los archivos. Consecuentemente, el cargador de clases que
carga los JARs del JNDI normalmente es el mismo que carga la aplicación y las
factorías.
Observa que no podemos usar los ficheros de recursos de apliación JNDI con
el JDK 1.1. Puedes ver la sección Ficheros
de Recurso de Aplicación para más detalles.
Cargar Clases en la Plataforma Java 2
Cuando usamos el JNDI con la plataforma Java 2, el cargador de clases que carga
las clases JNDI normalmente es diferente del que carga la aplicación, Por
ejemplo, en el Java 2 SDK, v1.3, las
clases JNDI las carga el cargador de clases bootstrap mientras que las
clases de la aplicación las carga el cargador de clases del sistema. En el Java
2 SDK, v1.2, si instalamos el JNDI como una extensión instalada, las clases
JNDI las carga el cargador de clases responsable de cargar las extensiones
instaladas, mientras que las clases de la aplicación las carga el cargador de
clases del sistema. Como resultado, si el JNDI usa su cargador de clases para
cargar los proveedores de servicio y las factorías, podría no ver las mismas
clases que la aplicación. Por lo tanto, para intentar ver lo mismo que la
aplicación puede ver, el JNDI usa el cargador de clases de contexto del thread
llamante cuando está cargando las clases para los proveedores de servicios, las
factorías y los ficheros de recursos de aplicación.
En raras circunstancias, querremos cambiar el cargador de clases del contexto
del thread para que afecte al modo de encontrar las clases JNDI. Esto podría
ocurrir, por ejemplo, cuando el entorno en el que estamos trabajando no ve
apropiadamente el cargador de clases de contexto. Por ejemplo, un bug en el Java
2 SDK, Standard Edition, v1.2.2 hace que el AWT (Abstract Window Toolkit) no
configure los oyentes del cargador de clases de contexto del thread para qu sea
el cargador de clases que carga el applet. Consecuentemente, lo métodos de
retrollamada invocados por los threads oyentes no tienen acceso al proveedor de
servicios ni a las factorías que el applet por otro lado puede cargar
explícitamente. O, podríamos querer añadir un repositorio adicional de
fichero JAR o class que contenga proveedores o factorías especiales para
nuestras aplicaciones o applets. Para cambiar el cargador de clases de contexto
del thread, usamos Thread.setContextClassLoader().
Aquí tenemos un ejemplo.
ClassLoader prevCl = Thread.currentThread().getContextClassLoader();
// Create the class loader by using the given URL
// Use prevCl as parent to maintain current visibility
ClassLoader urlCl =
URLClassLoader.newInstance(new URL[]{new URL(url)}, prevCl);
try {
// Save the class loader so that you can restore it later
Thread.currentThread().setContextClassLoader(urlCl);
// Expect that the environment properties are in the
// application resource file found at "url"
Context ctx = new InitialContext();
System.out.println(ctx.lookup("tutorial/report.txt"));
// Do something useful with ctx
...
} catch (NamingException e) {
// Handle the exception
} finally {
// Restore
Thread.currentThread().setContextClassLoader(prevCl);
}
Este ejemplo crea un URLClassLoader que carga clases desde una
URL específicada. Luego crea un contexto inicial y realiza otras operacioens
JNDI dentro del contexto de ese cargador de clases. En este ejemplo, el cargador
de clases sólo afecta al modo de inicialización del contexto inicial (mediante
un fichero de recursos de aplicación encontrado por el nuevo cargador de
clases), así como en el resultado de lookup() (mediante la
factoría de objetos nombrada en el fichero de recursos de aplicación y el
fichero class de la factoría encontrado por el nuevo cargador de clases. Para
ejecutar este programa, debemos suministrar la URL codebase de esta forma.
# java ChangeClassLoader file:/some/directory/somewhere/
En este codebase, podemos especificar un fichero jndi.properties.
En este ejemplo particular, este es el fichero
jndi.properties en
el directorio del codebase.
java.naming.factory.initial=com.sun.jndi.fscontext.FSContextFactory
java.naming.provider.url=file:/tmp
java.naming.factory.object=FooObjectFactory
En el mismo directorio codebase está la definición de la clase para FooObjectFactory,
que cuando se el da un objeto java.io.File siempre devuelve el
string "foo". Cuando ejecutamos este programa, veremos
esta salida.
foo
Si no vemos esta salida, debemos chequear la corrección del argumento URL.
Debemos recordar que si estamos nombrando un directorio codebase, debemos
incluir una barra inclinada al final de la URL.
Cargar Clases desde Localizaciones Pre-Especificadas
En algunos casos, la localización de los ficheros class se especifica
explícitamente. Por ejemplo, podemos especificar el codebase para una factoría
de objetos en su Reference.
Cuando el JNDI lea dicho objeto desde el servicio de nombres o de directorio,
usará un cargador de clases para el codebase nombrado en la Reference
para obtener los ficheros class de la factoría. El padre de este cargador de
clases es el cargador de clases de contexto del thread.