Download Tutorial sobre AWT y applets - dccia
Document related concepts
no text concepts found
Transcript
Tema 6 Programación de Applets 6.1 Fundamentos de los applets Un applet es un programa Java que se ejecuta en el navegador web, utilizando todos sus recursos como entorno de trabajo. Por ejemplo, la página en la que se carga el applet hace de entorno gráfico en el que se van a dibujar los comandos gráficos. Algunas ventajas de los applets frente a JavaScript son: Mayor potencia. Uso de funciones gráficas. Código fuente no visible. Opciones de seguridad. Suponemos que tienes un conocimiento inicial del lenguaje de programación Java. A continuación vamos a explicar las características básicas de los applets, utilizando ejemplos y demostraciones. 6.1.1 Implementación básica de un applet Para construir un applet debemos implementar una clase que especialice la clase Java Applet. La clase Applet permite la definición de pequeñas aplicaciones que van a ser ejecutadas dentro de un programa, en nuestro caso un navegador. Esta clase proporciona un conjunto de métodos de utilidad que veremos más adelante, y que nos van a permitir, por ejemplo, cargar una imagen desde una URL, reproducir un sonido o abrir una nueva página en el navegador. Ciclo de vida de un applet Cuando el navegador carga un applet por primera vez, éste debe realizar ciertas tareas de inicialización. En otros momentos, el applet desaparecerá de la vista del usuario, bien porque éste ha cargado otra página, o porque ha minimizado la página actual. En cualquier de estos casos, el navegador llama a un método del ciclo de vida del applet, para que éste realice las tareas correspondientes a cada caso. Los métodos son los siguientes: init(): método al que llama el navegador cuando se carga o recarga el applet. En este método debe incluirse todo el código de inicialización del mismo. start(): método al que llama el navegador cuando comienza a ejecutar el applet. Si un usuario sale de la página en la que se ha cargado el applet y vuelve a ella, el applet no se reinicia, sino que vuelve a llamarse a este método. stop(): método al que llama el navegador cuando se detiene el applet. Esto sucede cuando el usuario abandona la página con el applet, o minimiza la aplicación. destroy(): método al que llama el navegador justo antes de descargar el applet. Todos los métodos del ciclo de vida están definidos, por defecto, en la clase Applet. A la hora de implementar un applet se deberán redefinir únicamente aquellos métodos necesarios. Lo más usual es definir únicamente init() y dejar que el resto de métodos ejecuten el código de la clase padre. Tema 6. Programación de applets Applets como componentes AWT El AWT (Abstract Windows Toolkit) es un conjunto de clases de Java que permiten construir y manejar interfaces de usuario. Estas clases son parte integral del lenguaje y proporcionan, por lo tanto, una garantía de que la aplicación desarrollada va a poder ejecutarse en cualquier entorno que soporte el lenguaje Java. En nuestro caso, el entorno de ejecución será un navegador. El AWT se basa en dos conceptos fundamentales: componentes y eventos. Los componentes son los elementos como menús, botones, etiquetas, campos de texto, etc. que forman la interfaz de usuario. Una interfaz de usuario se construye añadiendo con el método add los componentes al applet, o añadiéndolos a paneles (objetos de la clase Panel) que después se añadirán a su vez al applet. En realidad, la clase Applet es una subclase de Panel, y ésta es subclase de Container (la jerarquía de clases de AWT se muestra en el apartado ...) Un objeto container es un tipo especial de componente no visible, que permite agrupar los componentes que se añaden en él y tratar como uno todos ellos cuando se insertan en un container superior (el applet, por ejemplo). Por otra parte, los eventos se producen como consecuencia de las acciones del usuario sobre los componentes y deben ser tratados por el programa. Para ello, el AWT 1.1proporciona los denominados listeners, objetos que se asocian a componentes y que manejan los eventos específicos de su clase. Hay distintas clases de listeners, por ejemplo: actionListener, para acciones semánticas como la pulsación de un botón, mouseListener, para eventos del ratón como pulsaciones, windowListener, para eventos relacionados con las ventanas, como cerrar o minimizar ventana, etc. Cada objeto escucha únicamente los eventos del tipo correspondiente a su clase y que además se han ejecutado sobre el componente al que están asociados. NOTA 2 En las distintas versiones de Java se ha ido modificando la librería AWT. En las primeras versiones (JDK 1.0X) se hablaba del AWT 1.0. Con el lanzamiento de la versión 1.1 de Java se introduce AWT 1.1 y se modifica sustancialmente el modelo de eventos de la librería. La introducción Java 2 ha provocado un cambio considerable también en las librerías de interface de usuario. Aunque se ha mantenido AWT 1.1 sin cambios, se ha introducido una versión nueva, Swing, que mantiene un modelo muy parecido de componentes y eventos, pero mejora sustancialmente los componentes. Existe una variedad mucho mayor de componentes, introduciéndose mejoras como la posibilidad de imágenes en los botones, las tablas o la posibilidad de cambiar la apariencia de las componentes (dándoles el aspecto Windows, Mac, Motif, etc.). En cuanto a la compatibilidad con los navegadores, si buscamos una compatibilidad absoluta tendremos que utilizar AWT 1.0. La versión 1.1 de AWT es soportada desde la versión 4 del Internet Explorer y del Netsape y es un buen compromiso de compatibilidad y funcionalidad. No existen navegadores compatibles con Swing, aunque es posible implementar applets que lo utilicen mediante el Java Plug-in de Sun Microsystem (ver apartado 6.3.3) Tema 1. Protocolo HTTP y aplicaciones web Un primer ejemplo de applet Ejemplo de un sencillo applet que muestra un botón y asocia una acción al evento de pulsar el botón. En este caso, la acción es ejecutar un beep. Listado 6.1. Beep.java Sencillo applet que muestra un botón y ejecuta un beep al pulsarlo import java.awt.*; import java.applet.*; import java.awt.event.*; public class Beep extends Applet { Toolkit toolkit; public void init() { toolkit = this.getToolkit(); Button botonBeep = new Button("Beep"); botonBeep.addActionListener(new BotonBeepListener()); add(botonBeep); } class BotonBeepListener implements ActionListener { public void actionPerformed(ActionEvent event) { toolkit.beep(); } } } En el applet se define el componente botonBeep de tipo botón y se le asocia un manejador de eventos de la clase BotonBeepListener. Esta clase es una clase interna (inner class) de Beep e implementa la interfaz ActionListener, que escucha el evento 'pulsación del botón'. Como ya hemos comentado, cuando se produce un evento sobre un componente sólo escuchan ese evento aquellos manejadores que están asociados al componente y que implementan métodos relacionados con el evento producido. En este caso, la interfaz ActionListener sólo tiene un único método, que es actionPerformed(), que se ejecuta cuando se pulsa el botón al que se ha asociado el manejador. Para comprobar el funcionamiento del applet, debes primero compilarlo. Para ello, debes tener instalado algún compilador Java. Supongamos que tienes el JSDK 1.3 de Sun, en el directorio C:\jdk1.3. Debes inicializar las variables del entorno necesarias para que el compilador y el intérprete de Java funcionen correctamente: > set JAVA_HOME = C:\jdk1.3 > set CLASSPATH = %JAVA_HOME%\lib;. > set PATH = %PATH%:%JAVA_HOME%\bin Ahora ya puedes compilar el programa > javac Beep.java y cargarlo en un navegador. El siguiente fichero HTML se encarga de realizar la carga del applet: Listado 6.2. Beep.html Documento HTML que carga el applet Beep.class en el navegador <html> <applet code=Beep.class width=100 height=100> </applet> </html> También es posible comprobar el funcionamiento de un applet utilizando el programa appletviewer proporcionado en la distribución del JSDK de Sun. 3 Tema 6. Programación de applets > appletviewer Beep.html Verás un botón que ejecutará un sonido cuando lo pulses. 6.1.2 El tag APPLET Ya hemos visto en el ejemplo anterior que la etiqueta que se emplea en la página HTML para cargar el applet es <APPLET>. Sintaxis La sintaxis completa de esta etiqueta es la siguiente: <APPLET [CODEBASE = URL] [CODE = fichero con la clase principal] WIDTH = anchura HEIGHT = altura [ALT = texto alternativo] [NAME = nombre del applet] [ALIGN = alineación en la página] [VSPACE = espacio vertical] [HSPACE = espacio vertical] > [<PARAM NAME = nombre del parámetro 1 VALUE = valor del parámetro 1 >] [<PARAM NAME = nombre del parámetro 2 VALUE = valor del parámetro 2 >] ... [Texto HTML alternativo] </APPLET> Descripción de los elementos El significado de cada uno de estos elementos se comenta en la siguiente tabla: 4 Elemento Descripción CODEBASE = URL URL base del applet. Por defecto, el URL del directorio que contiene la página desde la que se carga el applet. Si se especifica una dirección relativa, se añade a esta URL por defecto. CODE = fichero .class Nombre del fichero .class que implementa el applet. La URL completa del fichero se obtiene añadiendo este nombre al CODEBASE. WIDTH = pixels Ancho en pixels de la zona del navegador reservada para mostrar el applet HEIGHT = pixels Alto en pixels de la zona del navegador reservada para mostrar el applet. ALT = textoAlternativo Texto que debe mostrar el navegador si entiende la etiqueta APPLET, pero es incapaz de cargar el applet. NAME = nombre Nombre que identifica el applet. Se usa para que más de una applet en la misma página puedan comunicarse entre ellos. ALIGN = alineación Elemento idéntico al atributo del tag IMG de HTML. Define la alineación del applet. Puede tomar los siguientes valores: left, right, top, texttop, middle, absmiddle, baseline, bottom, absbottom. VSPACE = pixels Elemento idéntico al atributo del tag IMG de HTML. Espacio vertical en pixels que el navegador debe reservar entre el texto previo y el applet. Tema 1. Protocolo HTTP y aplicaciones web HSPACE = pixels Elemento idéntico al atributo del tag IMG de HTML. Espacio horizontal en pixels que el navegador debe reservar entre el texto previo y el applet. PARAM = identificador del parámetro VALUE = valor Se utiliza para el paso de parámetros desde la página HTML hasta el applet. Ya se ha explicado en detalle. Texto alternativo Texto que aparecerá en aquellos navegadores que no entiendan el tag APPLET. El elemento CODEBASE permite que el applet se encuentre en un directorio (incluso en una URL) distinto del directorio en el que se encuentra el fichero HTML desde el que se carga. De esta forma, podemos en el servidor organizar applets (ficheros .class) y ficheros HTML en directorios distintos. El atributo CODE debe contener el nombre del fichero principal que implementa el applet, esto es, el fichero con el nombre de la clase que es subclase de Applet. En el caso de que este fichero deba importar otras clases adicionales implementadas por nosotros, el navegador las cargará de forma automática realizando una petición HTTP por cada una de ellas. Esto introduce un retardo importante en la carga del applet. Para mejorar el rendimiento de estos casos, la versión 1.1 del JDK de Java introdujo los archivos JAR, basados en el formato ZIP, que comprimen un conjunto de ficheros CLASS en un único archivo. Con la etiqueta ARCHIVE se carga el fichero JAR en el navegador. Por ejemplo, para cargar el fichero JAR menu.jar, que está situado en el directorio applets y tiene una clase menu.class que es subclase de Applet, se debería escribir: <APPLET CODEBASE = applets/ CODE = menu.class ARCHIVE = menu WIDTH = 100 HEGHT = 400> Parámetros Otro elemento muy importante son las etiquetas PARAM. Constituyen la forma de pasar parámetros desde la página HTML hasta el applet. Normalmente los applets "profesionales" tienen un gran número de parámetros que permiten una gran flexibilidad en su uso, haciendo posible que el applet funcione en múltiples situaciones sin necesidad de reescribirlo ni recompilarlo. Todos los valores de los parámetros se pasan como strings y es el applet debe convertirlos en el tipo adecuado. Cuando escribamos un applet que use parámetros debemos documentar perfectamente qué parametros acepta y cuál es la sintaxis esperada para cada uno de los parámetros. Por ejemplo, revisemos el applet MenuApplet2L. Se trata de uno de los muchísimos applets "profesionales" de dominio público desarrollados para mostrar menús en páginas HTML. Hemos escogido éste por su sencillez, y por tener disponible el código fuente. En este caso, el applet permite dos niveles de profundidad en los menús y opciones de menús con gráficos y/o texto. NOTA: Sitios web donde buscar applets (jars, gamelan, tocows) www.jars.com Para cargar el applet, hay que especificar los valores de todos los parámetros necesarios para configurarlo, como son el color de fondo, el fuente por defecto del texto o todas las opciones de menú junto con la URL a la que apuntan. Listado 6.3: Ejemplo de etiqueta APPLET para configurar el applet MenuApplet2L <APPLET CODEBASE = "." CODE = "menuapplet2l.MenuApplet2L.class" ARCHIVE = "menuapplet2l.jar" NAME = "MenuApplet2L" WIDTH = 200 HEIGHT = 400 > <PARAM <PARAM <PARAM <PARAM NAME NAME NAME NAME = = = = "Background" VALUE = "FFFFFF"> "BackgroundSubMenu" VALUE = "FFFFFF"> "DefaultFont" VALUE = "Arial,,14"> "TextColor" VALUE = "003366"> 5 Tema 6. Programación de applets <PARAM NAME = "TextColorSubMenu" VALUE = "003366"> <PARAM NAME = "TextColorHighlight" VALUE = "4444FF"> <PARAM NAME = "TextColorPressed" VALUE = "FF4444"> <PARAM NAME = "Choice0" VALUE = "Universidad de Alicante >>"> <PARAM NAME = "Choice0-0" VALUE = "Home page (new frame)"> <PARAM NAME = "Choice0-0-URL" VALUE = "http://www.ua.es"> <PARAM NAME = "Choice0-0-FONT" VALUE = "Arial,,12"> <PARAM NAME = "Choice1" VALUE = "PAW"> <PARAM NAME = "Choice1-URL" VALUE = "http://www.dccia.ua.es/dccia/inf/asignaturas/PAW"> <PARAM NAME = "Choice2" VALUE = "Departamento DCCIA >>"> <PARAM NAME = "Choice2-0" VALUE = "Componentes"> <PARAM NAME = "Choice2-0-URL" VALUE = "http://www.dccia.ua.es/dccia/publico/estaticas/cas/componentes.cas"> <PARAM NAME = "Choice2-0-FONT" VALUE = "Arial,,12"> <PARAM NAME = "Choice2-1" VALUE = "Docencia"> <PARAM NAME = "Choice2-1-URL" VALUE = "http://www.dccia.ua.es/dccia/publico/estaticas/cas/docencia.cas"> <PARAM NAME = "Choice2-1-FONT" VALUE = "Arial,,12"> </APPLET> 6.1.3 Algo más de AWT 1.1 Componentes de AWT 1.1 Ya hemos visto que los componentes son los elementos que forman la interfaz de usuario de un applet. En AWT 1.1 existen 9 tipos de componentes: Button, Canvas, Checkbox, Choice, Label, List, Scrollbar, TextComponent (TextField y TextArea), MenuComponent (MenuItem y MenuBar). Además, existe un tipo especial de componente invisible pero fundamental para la visualización de todos los demás. Se trata de la clase Container, con subclases como Panel, Window o ScrollPane, que sirve para almacenar y distribuir espacialmente los componentes elementales. Como un container es también un componente, se puede anidar la utilización de unos y otros, por ejemplo añadiendo un panel que contenga un cierto número de botones en la parte izquierda de un applet, que, como ya hemos visto, es un tipo especial de panel. La siguiente figura muestra el aspecto visual de todos los componentes de AWT 1.1. La estructura de clases de los componentes de AWT 1.1 se muestra en la siguiente figura. 6 Tema 1. Protocolo HTTP y aplicaciones web Todos los componentes son subclases de la clase Component, y heredan alrededor de 100 métodos de esa clase. Algunos de métodos de esta clase que pueden resultar útiles son getSize(), getLocation(), getBounds(), setEnabled(boolean), setVisible(boolean), setBackground(color), setForeground(color) o setFont(Font). Se puede encontrar más información en la documentación de las clases de AWT incluida en la distribución del JSDK. Además de estos componentes existe un componente especial orientado a la construcción de menús. Se trata de MenuComponent, y se diferencia de los anteriores en que sólo es posible añadirlo a objetos de la clase Window. Dado que los applets no son objetos de esta clase, sino de la clase Panel, no es posible añadir directamente un menú en applet. En la siguiente tabla podemos ver un ejemplo de cada uno de los componentes mencionados. Componente Ejemplo Button Button comp = new Button("Etiq"); Canvas class RegionDibujo extends Canvas { public RegionDibujo() { setSize(100, 50); } public void paint(Graphics g) { g.drawRect(0, 0, 99, 49); // dibujo el borde g.drawString("Un Canvas", 20,20); } } RegionDibujo comp = new RegionDibujo(); Checkbox Checkbox m = comp Checkbox("Etiq", true); Grupo de checkboxs (radio buttons) CheckboxGroup cbg = new CheckboxGroup(); Checkbox comp1 = new Checkbox("Etiq 1", cbg, true); Checkbox comp2 = new Checkbox("Etiq 2", cbg, false); Choice Choice comp = new Choice(); comp.add("Etiq 1"); comp.add("Etiq 2"); comp.add("Etiq 3"); Label Label comp = new Label("Etiq"); 7 Tema 6. Programación de applets List List list = new List(3, false); // Visualizo 3 list.add("Etiq 1"); // elementos, list.add("Etiq 2"); // sin selección list.add("Etiq 3"); // múltiple list.add("Etiq 4"); list.add("Label 5"); Scrollbar Scrollbar comp = new Scrollbar(Scrollbar.HORIZONTAL, 0, // valor inicial 5, // ancho del slider -100, 100); // rango Text comp = new Text("Etiq",15); // número de columnas TextField TextArea TextArea comp = new TextArea("Etiq", 10,20); // filas y cols. Para comprobar el funcionamiento de cualquiera de los componentes de la tabla anterior, no hay más que insertar el código ejemplo en el método init() del applet y añadir el componente al applet: import java.awt.*; import java.applet.Applet; public class Test extends Applet { public void init() { // Insertar el código ejemplo aquí add(comp); } } En el caso del Canvas, hay que realizar la definición de la clase RegionDibujo fuera de la clase Test: import java.awt.*; import java.applet.Applet; public class Test extends Applet { public void init() { // Insertar el código ejemplo aquí add(comp); } } public class RegionDibujo extends Canvas { // Completar código } Contenedores y gestores de disposición Hemos mencionado que para construir la interfaz de usuario se deben ir añadiendo componentes en el applet, un objeto de clase Container. El método utilizado para ello es el método add: add(comp1); add(comp2); ... add(compN) ¿Cómo aparecen ordenados los componentes en el applet? Depende de la gestión de disposición (layout management) activa en el contenedor. La gestión de la disposición es el proceso de determinar el tamaño y la posición de los componenentes. Por defecto, cada contenedor tiene activo un gestor de disposición que es el que realiza esa tarea con los componentes que se van añadiendo al contenedor. La librería AWT 1.1 proporciona fundamentalmente cuatro gestores de disposición: BorderLayout, FlowLayout, GridLayout y GridBagLayout. Siempre que se use el método add para añadir un componente en un contenedor, tiene que tenerse en cuenta el gestor de disposición. Algunos 8 Tema 1. Protocolo HTTP y aplicaciones web gestores, como BorderLayout, necesitan un parámetro adicional en el método add indicando dónde colocar el componente (en el norte, sur, este, oeste o centro). Otros gestores, como GridBagLayout, requieren un complicado proceso de preparación. Y otros, como FlowLayout o GridLayout, simplemente van colocando los componentes basándose en el orden en que son añadidos en el contenedor. También es posible definir una posición y tamaño absoluta para los componentes. Esto sólo se debe hacer cuando la aplicación no va a reutilizarse, va a tener siempre el mismo tamaño y controlamos totalmente aspectos que influyen en el tamaño final de los componentes como es el tamaño de las fuentes utilizadas. Manejo de eventos en AWT 1.1 En el modelo de eventos de AWT 1.1 (y también de SWING) los eventos se generan desde fuentes de eventos (event sources). En concreto, estas fuentes de eventos son los componentes que constituyen la interfaz de usuario. Uno o más oyentes (listeners) de eventos pueden registrarse para ser avisados cuando se produce un evento de una determinada clase ha sido generado por una fuente determinada. En AWT 1.1 los oyentes de eventos se definen mediante interfaces, lo que permite que cualquier clase pueda manejar eventos, siempre que implemente la interfaz correspondiente. Normalmente, cualquier programa que implemente un manejador de eventos hará lo siguiente: Definir una clase que implemente la interfaz del oyente de eventos deseado. Por ejemplo: class MiClase implements ActionListener { Implementar en esa clase los métodos que van a manejar los eventos que se reciban: public void actionPerformed (ActionEvent e) { // código que gestiona el evento recibido } Registrar un objeto de esta clase como oyente en uno o más componentes que actuarán de fuentes de eventos: componente.addActionListener(instanciadeMiClase); En la figura siguiente podemos ver un ejemplo de registro de oyentes. En ella se representan tres objetos del tipo oyente (listener) que se registran en dos componentes. Los objetos oyente1 y oyente2 se registran en componente1, y oyente2 y oyente3 se registran en componente2. Registro componente1 oyente1 Registro Registro oyente2 componente2 Registro oyente3 9 Tema 6. Programación de applets Una vez registrados, cualquier evento que se produzca en un determinado componente (movimiento del ratón, selección de una opción, pulsación del ratón, etc) será enviado a los oyentes registrados en ese componente para que lo procesen. En la siguiente figura se muestra este comportamiento. Eventos componente1 oyente1 Eventos Eventos oyente2 componente2 Eventos oyente3 Aunque esto es bastante sencillo, la flexiblidad de la librería AWT hace que podamos encontrar bastantes formas de implementar este modelo. Como ejemplo curioso, veamos dos implementaciones adicionales del applet Beep del listado 6.1: En el primer ejemplo se muestra cómo es posible extender la clase Button incorporando el manejo de los eventos en la propia clase. De esta forma, cualquier objeto nuevo que creemos de la clase BotonBeep tendrá por defecto asociado la ejecución de un beep cuando lo pulsemos public class Beep2 extends Applet { public void init() { BotonBeep botonBeep = new BotonBeep("Beep"); add(botonBeep); } } class BotonBeep extends Button implements ActionListener { public BotonBeep(String str) { super(str); addActionListener(this); } public void actionPerformed(ActionEvent event) { Component c = (Component)event.getSource(); c.getToolkit().beep(); } } El segundo ejemplo es el preferido por algunos tutoriales que desean ahorrarse unas cuantas líneas de código. Se basa en crear un objeto in-line que será el encargado de manejar el evento de pulsación del botón. public class Beep3 extends Applet { public void init() { Button botonBeep = new Button("Beep"); botonBeep.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { Component c = (Component)event.getSource(); c.getToolkit().beep();}}); add(botonBeep); } } 10 Tema 1. Protocolo HTTP y aplicaciones web Para hacer más eficiente el sistema se implementan distintas clases de eventos, que sólo serán escuchados por los objetos de las clases que implementen el interfaz correspondiente. Por ejemplo, un evento de clase KeyEvent (pulsación de una tecla) sólo será enviado a objetos que implementen la interfaz KeyListener. Los nombres de los tipos de evento y de las interfaces se construyen siempre igual. Las clases de eventos se denominan XXXXEvent y las interfaces se denominan XXXXListener. La siguiente tabla enumera los tipos de evento, las interfaces y los métodos. Evento Eventos de bajo nivel Eventos semánticos Listener Métodos componentHidden componentMoved ComponentEvent ComponentListener componentResized componentShown componentAdded ContainerEvent ContainerListener componentRemoved focusGained FocusEvent FocusListener focusLost keyPressed KeyEvent KeyListener keyReleased keyTyped mouseClicked mouseEntered mouseExited MouseEvent MouseListener mousePressed mouseReleased mouseDragged MouseMotionEvent MouseMotionListener mouseMoved windowActivated windowClosed windowClosing windowDeactivated WindowEvent WindowListener windowDeiconified windowIconofied windowOpened ActionEvent ActionListener actionPerformed AdjustmentEvent AdjustmentListener adjustmentValueChanged ItemEvent ItemListener itemStateChanged TextEvent TextListener textValueChanged Cada componente puede generar un determinado tipo de eventos. En la tabla siguiente se enumeran los tipos de evento que puede generar cada uno de los componentes de AWT. Hay que hacer notar que cuando una clase de componente genera un tipo de evento, también lo hacen todas sus subclases. Componente Tipo de evento generado Component ComponentEvent FocusEvent KeyEvent MouseEvent MouseMotionEvent ContainerEvent WindowEvent ActionEvent ActionEvent ItemEvent ActionEvent TextEvent ItemEvent ItemEvent AdjustmentEvent Eventos de bajo nivel Container Window Button List TextField Eventos semánticos Choice Checkbox Scrollbar 11 Tema 6. Programación de applets Ejemplos Para terminar el apartado dedicado a AWT, vamos a presentar un par de ejemplos en los que se utilizan los conceptos que hemos ido presentando. En el primer ejemplo, se muestran diversos componentes AWT y se juega con los eventos que producen. Listado 6.4 EjemploAWT.java Applet que muestra diversos componentes AWT import java.awt.*; import java.applet.*; import java.awt.event.*; public class EjemploAWT extends Applet { Choice menu; Checkbox cb1, cb2, cb3; TextField text; TextArea output; public void init() { menu = new Choice(); menu.add("Opción 1"); menu.add("Opción 2"); menu.add("Opción 3"); menu.add("Opción 4"); MiItemListener il = new MiItemListener(); menu.addItemListener(il); add(menu); cb1 = new Checkbox("Checkbox 1"); cb1.addItemListener(il); cb2 = new Checkbox("Checkbox 2"); cb2.addItemListener(il); cb3 = new Checkbox("Checkbox 3"); cb3.addItemListener(il); add(cb1); add(cb2); add(cb3); text = new TextField("Escribe algo aquí"); add(text); Button boton = new Button("Escribir"); boton.addActionListener(new BotonListener()); add(boton); output = new TextArea(10, 40); output.setEditable(false); add(output); } class BotonListener implements ActionListener { public void actionPerformed(ActionEvent event) { String str = menu.getSelectedItem(); if (cb1.getState()) str = str + " " + cb1.getLabel(); if (cb2.getState()) str = str + " " + cb2.getLabel(); if (cb3.getState()) str = str + " " + cb3.getLabel(); str = str + " " + text.getText(); output.append(str+"\n"); } } class MiItemListener implements ItemListener { public void itemStateChanged(ItemEvent event) { Object source = event.getSource(); if (source instanceof Choice) output.append(((Choice)source).getSelectedItem()+" seleccionada\n"); else if (source instanceof Checkbox) output.append(((Checkbox)source).getLabel()+" ha cambiado\n"); } } } Podemos ver cómo se definen dos clases que manejan eventos: BotonListener y MiItemListener. Ambas clases son internas a EjemploAWT y pueden, por tanto, acceder a variables de esa clase. En este caso ambas clases acceden a los componentes para obtener su estado e imprimirlo en output. La clase BotonListener implementa un manejador que describe el estado de menu, cb1, cb2, cb3 y text. Un objeto de esta clase se registra en el 12 Tema 1. Protocolo HTTP y aplicaciones web componente botón. La clase MiItemListener implementa un manejador que se activa cuando cambia de estado algún item. Un único objeto de esta clase se registra en todos los componentes que generan un evento de tipo item. En el segundo ejemplo mostramos cómo se puede utilizar un Canvas y gestionar los eventos de ratón para implementar un applet que permite activar y desactivar puntos situados sobre una rejilla. Este applet podría ser la semilla de una aplicación más ambiciosa que implemente un juego de "guerra de barcos". Listado 6.5. DibujaPuntos.java Applet que permite activar y desactivar puntos en una rejilla import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class DibujaPuntos extends Applet { public void init() { Dimension d = getSize(); AreaDibujo region = new AreaDibujo(d.width,d.height); add(region); } } class AreaDibujo extends Canvas implements MouseListener{ boolean puntos[][] = new boolean[10][10]; public AreaDibujo(int ancho, int alto) { for (int i=0; i<10;i++) for (int j=0; j<10; j++) puntos[i][j] = false; setSize(ancho,alto); addMouseListener(this); // La propia clase implementa la // interfaz MouseListener y maneja // los eventos del raton } // Metodo de la interfaz MouseListener public void mousePressed(MouseEvent e) { int i = e.getX()/10; int j = e.getY()/10; if (puntos[i][j]) puntos[i][j] = false; else puntos[i][j] = true; repaint(); } // Los public public public public siguientes metodos están vacíos y completan la interfaz MouseListener void mouseClicked(MouseEvent e) {} void mouseEntered(MouseEvent e) {} void mouseExited(MouseEvent e) {} void mouseReleased(MouseEvent e) {} // Reescritura del metodo paint de la clase Canvas public void paint(Graphics g) { System.out.println("Paint"); for (int i=0; i<10; i++) for (int j=0; j<10; j++) if (puntos[i][j]) { g.setColor(Color.blue); g.fillOval(i*10+3, j*10+3,6,6); g.setColor(Color.red); g.drawOval(i*10+3, j*10+3,6,6); } } } 13 Tema 6. Programación de applets Para cargar el applet: Listado 6.6. DibujaPuntos.html Documento HTML que carga el applet DibujaPuntos.class en el navegador <html> <applet code=DibujaPuntos.class width=100 height=100> </applet> </html> 6.2 Métodos del paquete java.applet El paquete java.applet está constituido por la clase Applet y las interfaces AppletContext, AppletStub y AudioClip. Las interfaces son implementadas por el propio navegador y constituyen la forma de pasar información propia del navegador hacia el applet. Entre las funciones que vamos a poder realizar utilizando estos métodos se encuentran las siguientes: Obtener la URL donde se encuentra el documento que ha cargado el applet. Leer los valores los parámetros de la etiqueta APPLET del documento HTML. Obtener una imagen desde una determinada URL. Ejecutar un clip de sonido. Mostrar información en la línea de status del navegador. Abrir una página en el navegador. También se encuentran en la clase Applet un conjunto de métodos invocados desde el navegador cuando se produce un evento que puede afectar a la ejecución del applet, como es que el usuario cambie la página actual o que se minimize la ventana en la que se está ejecutando. Son los métodos init(), start(), stop() y destroy(). 6.2.1 Métodos invocados desde el applet La siguiente tabla enumera todos los métodos que podemos utilizar desde un applet. Son métodos de la propia clase Applet o de la interfaz AppletContext. Para utilizar un método de la interfaz AppletContext no tenemos más que obtener el appletContext asociado a un applet con el método getAppletContext(). Clase Applet Interface AppletContext 14 public public public public public public public public public public public public public URL getDocumentBase() URL getCodeBase() String getParameter(String nombre) AppletContext getAppletContext() void resize(int ancho, int alto) void showStatus(String msg) Image getImage(URL url) final static AudioClip newAudioClip(URL url) AudioClip getAudioClip(URL url) AudioClip getAudioClip(URL url, String nombre) String getAppletInfo() void play(URL url) void play(URL url, String nombre) AudioClip getAudioClip(URL url) Image getImage(URL url) Applet getApplet(String nombre) Tema 1. Protocolo HTTP y aplicaciones web Enumeration getApplets() void showDocument(URL url) public void showDocument(URL url, String target) void showStatus(String status) URL del documento y del código base Los métodos getDocumentBase() y getCodeBase() devuelven la URL del documento HTML que ha cargado el applet y la URL base desde la que se ha cargado el applet. Por ejemplo: URL base = getDocumentBase(); Image imagen = getImage(base, "image/image1.gif"); En el ejemplo anterior se obtiene la URL donde está instalado el documento HTML desde el que se ha cargado el applet y a continuación se obtiene la imagen image1.gif situada en el subdirectorio image de esa URL. De esta forma se evita dar una URL absoluta y se independiza la implementación del applet de su ubicación en el servidor. Obtención de parámetros de la etiqueta APPLET Recordemos que la sintaxis de la etiqueta APPLET para especificar un parámetro para el applet era: <PARAM NAME = nombre VALUE = valor> El método getParameter(string nombre) devuelve el string valor asociado al parámetro nombre en la etiqueta APPLET que ha cargado el applet. Después de obtenido el string, normalmente se convertirá en un objeto de otra clase (int, URL, Color, etc.) utilizando algún constructor de Java. Por ejemplo, supongamos un applet que permite configurar su color de fondo con una instrucción como: <PARAM NAME = "COLOR" VALUE = "00FF44" Para leer el valor de este parámetro en el applet se debe implementar el siguiente código: String colorStr = getParameter("COLOR"); if (colorStr == NULL) Color color = new Color(Color.white); else Color color = new Color(Integer.ParseInt(colorStr,16)); Mensajes en la línea de estado del navegador El método showStatus(String msg) permite mostrar un mensaje en la línea de estado del navegador. Esta línea no tiene posibilidad de scroll, por lo que cada nuevo mensaje borra el anterior. Por ejemplo: String link = getLinkCurrentOption(); showStatus(getDocumentBase()+link); El ejemplo anterior utiliza la línea de estado para mostrar una URL absoluta. En este caso, suponemos que el applet implementa un sistema de menús y que el método getLinkCurrentOption() devuelve el link asociado a la opción sobre la que se encuentra el ratón. Imágenes desde URLs Los métodos getImage(URL url) y getImage(URL url, string nombre) sirven para obtener un objeto de la clase Image que se encuentra en una determinada URL. Normalmente será la misma URL donde se encuentra el documento HTML que ha cargado el applet, o algún subdirectorio. La segunda forma de llamar al método suele ser la más usada desde los applets, utilizando getDocumentBase() para obtener la URL: 15 Tema 6. Programación de applets Image img = getImage(getDocumentBase(), "images/img1.gif"); El siguiente applet crea un Canvas en el que se dibuja una imagen que se ha cargado del directorio en el que se encuentra la página HTML: Listado 6.7 DibujaImagen.java Applet que carga la imagen "img1.gif" que debe existir en el mismo directorio en que se encuentra el fichero HTML que lo carga import java.applet.Applet; import java.awt.*; public class DibujaImagen extends Applet { public void init() { Image img = getImage(getDocumentBase(),"img1.gif"); Dimension d = getSize(); AreaDibujo region = new AreaDibujo(img, d.width, d.height); add(region); } } class AreaDibujo extends Canvas { Image img; public AreaDibujo(Image img, int ancho, int alto) { this.img = img; setSize(ancho,alto); } public void paint(Graphics g) { g.drawImage(img,0,0,this); } } Existen también otras versiones del método drawImage de la clase Graphics en las que, por ejemplo, es posible escalar la imagen ajustándola al tamaño del canvas. Nosotros hemos escogido la versión más sencilla, la que coloca la imagen directamente en la posición (0,0) del canvas. Si la imagen fuera más grande que el tamaño del canvas (que es el mismo que el del applet) aparecería recortada. Reproduciendo sonidos Existen un conjunto de métodos que permiten cargar y reproducir sonidos en los applets. Los sonidos deben ser de formato ".au", un formato propio de Sun. Es bastante habitual que los programas de tratamiento y edición de sonidos ofrezcan la opción de convertir los sonidos al formato ".au". Existen métodos en el paquete java.applet para cargar sonidos desde una URL y para reproducirlos. Son los siguientes: Para cargar un sonido desde una URL se utilizan los métodos getAudioClip(URL url) y getAudioClip(URL url, String, nombre). Al igual que con las imágenes, es posible pasar como parámetro una URL absoluta, o una URL base y un nombre de fichero que se añade a la URL base. Ambos métodos devuelven un objeto de tipo AudioClip. Para reproducir un sonido desde una URL se utilizan los métodos play(URL url) y play(URL url, String nombre). Una vez que se tiene un objeto de tipo AudioClip es posible reproducir el sonido una vez, reproducirlo de forma continua o detener la reproducción. Para ello se utilizan los métodos play(), loop() y stop(). El siguiente ejemplo presenta un applet con dos botones, uno para reproducir de forma continua un sonido cargado y el otro para detenerlo. El sonido se llama "train.au" y debe estar en el mismo directorio del fichero HTML que carga el applet. 16 Tema 1. Protocolo HTTP y aplicaciones web Listado 6.8 Sonido.java Applet que ejecuta un sonido import java.awt.*; import java.applet.*; import java.awt.event.*; public class Sonido extends Applet { AudioClip train; public void init() { train = getAudioClip(getDocumentBase(),"train.au"); Button botonPlay = new Button("Play"); botonPlay.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { train.loop();}}); add(botonPlay); Button botonStop = new Button("Stop"); botonStop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { train.stop();}}); add(botonStop); } } Abriendo páginas nuevas en el navegador La interface AppletContext proporciona un conjunto de métodos que permiten acceder a funcionalidades del entorno en el que se está ejecutando el applet. De hecho, las funciones ya comentadas de los applets para cargar imágenes o sonidos, se implementan obteniendo el AppletContext asociado al applet y llamando a los métodos proporcionados por esta interfaz. Dos de los métodos exclusivos de la interfaz, no implementados en la clase Applet, son showDocument(URL url) y showDocument(URL url, String target). Estos métodos hacen que el navegador abra una URL. El primer método lo hace en una nueva página, y el segundo permite especificar el destino del documento mediante los siguientes valores: "_blank": muestra el documento en una página nueva sin nombre "nombre": muestra el documento en una página nueva llamada "nombre" "_self": muestra el documento en la página y frame que contiene el applet "_parent": muestra el documento en la ventana del applet, pero en el frame padre del frame en el que se encuentra el applet. Si el frame en el que se encuentra el applet no tiene padre, se comporta igual que "_self". "_top": muestra el documento en la ventana del applet, pero en el frame de nivel superior. Si el frame en el que se encuentra el applet es el frame de nivel superior, se comporta igual que "_self". Como ejemplo listamos a continuación un applet que permite al usuario introducir una URL en un campo de texto y abre el documento en una nueva página cuando se pulsa ENTER o el botón. Listado 6.9 DocumentoNuevo.java Applet que prueba el funcionamiento del método showDocument import import import import java.awt.*; java.applet.*; java.awt.event.*; java.net.*; public class DocumentoNuevo extends Applet { TextField textField; AppletContext appletContext; 17 Tema 6. Programación de applets public void init() { appletContext = getAppletContext(); textField = new TextField("Introduce la URL aquí"); AbrirURL listener = new AbrirURL(); textField.addActionListener(listener); add(textField); Button botonURL = new Button("Abrir URL"); botonURL.addActionListener(listener); add(botonURL); } class AbrirURL implements ActionListener { public void actionPerformed(ActionEvent event) { String urlString = textField.getText(); URL url = null; try { url = new URL(urlString); } catch (MalformedURLException e) { showStatus("URL incorrecta: " + urlString); } if (url != null) appletContext.showDocument(url,"página nueva"); } } } Nótese el modo en que se comprueba que la URL es correcta, capturando la excepción MalformedURLException. También es interesante hacer notar que se asocia el mismo oyente del evento ActionEvent tanto al botón como al campo de texto, ya que se debe hacer lo mismo cuando se pulsa el botón o cuando se pulsa ENTER. 6.2.2 Métodos que se invocan desde el navegador Un conjunto de métodos de la clase Applet se invocan desde el navegador, respondiendo a acciones del usuario, como son recargar la página en la que se encuentra el applet o minimizar su ventana. Tal y como hemos mencionado previamente, estos métodos constituyen lo que se denomina el ciclo de vida del applet. Son los métodos init(), start(), stop() y destroy(). Para comprobar el funcionamiento de estos métodos, el siguiente applet los reescribe todos, haciendo que cuando el navegador llame a cualquiera de ellos se añada en un área de texto una cadena indicando qué método ha sido llamado. Se ha incluido también el método paint(), también interesante, aunque es un método heredado de la clase Component. Listado 6.10 SimpleApplet.java Applet que comprueba los métodos init, start, stop, destroy y paint import java.applet.Applet; import java.awt.*; public class SimpleApplet extends Applet{ static TextArea text; public void init() { if (text == null) { text = new TextArea(20,20); text.setEditable(false); } add(text); text.append("Soy un sencillo applet\n"); text.append("init...\n"); } public void start() { text.append("start...\n"); } public void stop() { text.append("stop...\n"); 18 Tema 1. Protocolo HTTP y aplicaciones web } public void destroy() { text.append("destroy...\n"); } public void paint(Graphics g){ text.append("paint...\n"); } } Es interesante comprobar el funcionamiento de este applet en distintos navegadores y en el appletviewer. Al cargar el applet todos ellos mostrarán: Soy un sencillo applet... init... start... paint... Sin embargo, en lo que se refiere a las paradas y recargas del applet podemos comprobar que en muchos casos el comportamiento es distinto del especificado. Por ejemplo, en el Internet Explorer 5.0, si pulsamos el botón "reload" o si accedemos a otra página y pulsamos el botón de "atrás", el applet muestra la secuencia: ... stop... destroy... Soy un sencillo applet... init... start... Esta secuencia no sigue la especificación original, ya que si salimos de la página y volvemos a entrar en ella, el applet debería detenerse y volver a comenzar. Esto es: ... stop... start... Puedes probar también qué sucede si minimizamos la página donde se muestra el applet. 6.3 Seguridad y restricciones de ejecución 6.3.1 Restricciones en la ejecución de los applets Un appet que ha sido descargado desde una determinada URL no puede hacer ninguna de las siguientes cosas: No puede cargar ni definir librerías nativas No puede leer ni escribir ficheros del computador en el que se encuentra el navegador que lo está ejecutando No puede establecer conexiones con otros computadores distintos del que originalmente ha servido el applet No puede lanzar ningún programa en el host que lo está ejecutando No puede leer ciertas propiedades del sistema Las ventanas que abre un applet deben ser distintas de las que abra una aplicación. Cada navegador tiene un objeto de la clase SecurityManager que es el encargado de implementar políticas de seguridad. Cuando un SecurityManager detecta una violación de la política, envía una excepción del tipo SecurityException. Por ejemplo, el siguiente listado muestra un applet que intenta escribir un fichero llamado "writetest". En él se captura la excepción y se muestra un mensaje de error en el que se reproduce la misma. 19 Tema 6. Programación de applets Listado 6.11 WriteFile.java Applet que intenta escribir un fichero en el disco local import import import import java.awt.*; java.io.*; java.lang.*; java.applet.*; public class WriteFile extends Applet { String myFile = "writetest"; File f = new File(myFile); DataOutputStream dos; public void init() { Label etiq = new Label(""); add(etiq); String osname = System.getProperty("os.name"); try { dos = new DataOutputStream(new BufferedOutputStream( new FileOutputStream(myFile),128)); dos.writeChars("Como mola el curso PAW\n"); dos.flush(); etiq.setText("Escritura correcta del fichero " + myFile); } catch (SecurityException e) { etiq.setText("Excepción de seguridad: "+ e); } catch (IOException ioe) { etiq.setText("Excepción de entrada/salida: " + ioe); } } } 6.3.2 Seguridad en Java 2 En el modelo de seguridad de Java 2 cualquier programa, ya sea local o remoto, está sometido a una política (policy) de seguridad. Esta política define el conjunto de permisos que se asocian a lugares (dominios de internet) o firmas digitales, de forma que cualquier código que se importe de un determinado lugar o venga con una determinada firma digital tiene asociado los permisos del lugar o la firma. Cada permiso especifica tipos de acceso a un determinado recurso, como permiso de lectura o escritura para un determinado fichero o directorio o permiso de acceso a un determinado host. El usuario o el administrador del sistema puede configurar estos permisos con una variedad de programas que proporciona la distribución del JSDK. Aparte de estas funcionalidades relacionadas con permisos, Java 2 proporciona acceso a una gran variedad de servicios relacionados con criptografía, como son algoritmos de firmas digitales, algoritmos de generación de claves, servicos de DSA (Digital Signature Algorithm), etc. Vamos a ver a continuación cómo configurar el fichero de política de seguridad del sistema para permitir que el applet anterior escriba el fichero writetest. El objetivo final va a ser otorgar un permiso de escritura a este nombre de fichero. Pueden existir varios ficheros de política de seguridad. Todos ellos se definen en el fichero JAVA_HOME\jre\lib\security\java.security Todos se especifican con de la siguiente forma: policy.url.n = URL Por defecto, existen las dos líneas siguientes: 20 Tema 1. Protocolo HTTP y aplicaciones web policy.url.1=file:${java.home}/lib/security/java.policy policy.url.2=file:${user.home}/.java.policy En estas líneas {java.home} hace referencia al directorio donde está instalado JRE, y {user.home} hace referencia al directorio home del usuario, que en Windows es el directorio C:\Windows. Aunque podríamos añadir tantas líneas como configuraciones de seguridad quisiéramos, vamos a simplificar el ejemplo y modificar el fichero .java.policy del directorio del sistema C:\Windows. Para ello debemos ejecutar el programa policytool en el directorio JAVA_HOME\jre\bin: > C:\jdk1.3\jre\bin\policytool Este programa permite editar ficheros de política, añadiendo o modificando permisos a recursos. Vamos a añadir un permiso de escritura al fichero writetest y vamos a otorgar este permiso a todo el mundo (es posible restringir el permiso sólo a una URL o a una firma digital). Para ello debemos seguir los siguientes pasos: Seleccionar la opción AddPolicyEntry. Dejamos CodeBase y SignedBy en blanco. El primer campo sirve para otorgar permiso sólo a una URL, y el segundo a una firma digital. Seleccionar AddPermission. Rellenar los campos Permission: con la opción FilePermission, TargetName: con writetest y Actions: con write. OK, Done y salvar como: .java.policy Ahora volvemos a ejecutar el appletviewer y veremos que el fichero ya se deja escribir. 6.3.3 Utilización del Java plug-in de Sun Los navegadores más usados (Internet Explorer y Netscape) implementan una máquina virtual Java que está bastante anticuada y que no incorpora las nuevas características y funcionalidades de las nuevas versiones de Java que va desarrollando Sun Microsystems. Para no tener que depender de las decisiones de las compañías que producen estos navegadores, Sun ha desarrollado el Java Plug-in, un plug-in que se instala en estos dos navegadores y que implementa la última máquina virtual Java desarrollada. De esta forma los applets ejecutados en esta máquina virtual pueden acceder a las últimas funcionalidades de Java, como por ejemplo el uso de Swing en las interfaces de usuario, o el manejo de la política de seguridad de Java. Normalmente, la instalación del Java Plug-in es recomendable en entornos de intranet, donde podemos tener control de las herramientas que se instalan en los equipos de los clientes, y en situaciones en las que es obligado usar alguna nueva característica de la última versión de Java en los applets que hemos desarrollado. Esta instalación es muy sencilla. Las últimas versiones del Internet Explorer y del Netscape incluso la realizan de forma automática cuando deben mostrar una página HTML que requiere el Java Plug-in. En estos momentos la última versión disponible del Java Plug-in es la 1.3. Esta versión es compatible con la última versión de Java, la Java 2 SDK, v. 1.3. Algunas de las ventajas de utilizar la versión Java 2 son: Uso de Swing para las interfaces de usuario de los applets. Este entorno es mucho más flexible que AWT. 21 Tema 6. Programación de applets Características de seguridad muy avanzadas. Por ejemplo, la posiblidad de utilizar certificados RSA para firmar digitalmente los applets. Conversión de HTML para cargar applets en el Java Plug-in La etiqueta APPLET hace una llamada a la máquina virtual Java instalada en el navegador. Si deseamos que un applet sea interpretado por el Java Plug-in es necesario utilizar una nueva etiqueta. Además esta etiqueta es distinta en el Internet Explorer y en el Netscape Navigator. En el primero debemos utilizar la etiqueta OBJECT, y en el segundo la etiqueta EMBED. Una forma muy sencilla de convertir la etiqueta APPLET es utilizando el programa HTML Converter desarrollado por Sun. Este programa está escrito en Java y transforma las etiquetas APPLET de los ficheros HTML que le especificamos en etiquetas para que los applets se ejecuten desde el Java Plug-in. Para instalar la herramienta debemos descomprimir el fichero htmlconv.zip en algún directorio, por ejemplo, en C:\programas-java. Después debemos añadir el nuevo directorio creado que contiene las clases del programa en la variable CLASSPATH: > CLASSPATH = %CLASSPATH%;C:\programas-java\converter\classes Ahora ya podemos situarnos en el directorio donde se encuentra el fichero html que deseamos convertir y llamar al programa HTMLConverter: > cd C:\applets\WriteFile > java HTMLConverter WriteFile.html El programa HTMLConverter guarda entonces el archivo original sonido.html en un nuevo directorio C:\applets\WriteFile_BAK y lo sustituye por un archivo que contiene las nuevas etiquetas. También es posible llamar al programa sin especificar ningún parámetro para que se lance una versión del programa con interfaz de usuario. Una vez que se ha convertido el fichero HTML, el navegador llamará al Java Plug-in y hará que éste ejecute el applet. Comprobar cómo ahora el applet WriteFile sí que puede escribir el fichero writetest. 22