Download arquitecturas software de varios niveles en java
Document related concepts
no text concepts found
Transcript
1 INGENIERÍA del SOFTWARE Curso 2006/07 Ingeniería en Informática Facultad de Informática UPV/EHU Departamento de Lenguajes y Sistemas Informáticos A. Goñi. Dpto. LSI, UPV/EHU 2 Tema 2: Arquitecturas Software de varios niveles en Java A. Goñi. Dpto. LSI, UPV/EHU 3 Índice • Arquitecturas Software de varios niveles • Construcción de interfaces gráficas en Java: AWT y Swing • Conexión con BDs relacionales en Java: JDBC • Persistencia de objetos en Java: mecanismos de serialización • Introducción a la computación distribuida en Java: RMI • Tecnología Java para la construcción de aplicaciones web: Applets, JSPs • Servicios Web A. Goñi. Dpto. LSI, UPV/EHU 4 1. Arquitecturas Software de varios niveles A. Goñi. Dpto. LSI, UPV/EHU 5 Índice • Introducción • Arquitectura lógica del software en niveles: presentación, lógica del negocio y datos. • Arquitectura física en 2 niveles: cliente gordo/servidor flaco • Arquitectura física en 2 niveles: cliente flaco/servidor gordo • Arquitectura física en 3 (o más) niveles A. Goñi. Dpto. LSI, UPV/EHU 6 Introducción • Algunas aplicaciones deben soportar a varios usuarios que de manera concurrente, segura, fiable y eficiente ejecutan varias operaciones. • Ejemplos: – bancos con cajeros conectados a un servidor central – distribuidores de ventas al por menor – terminales donde se pueden comprar entradas para espectáculos • En estos casos es útil una arquitectura con despliegue de componentes software en el lado del servidor (server side component architecture) – Componente: código que implementa un conjunto conocido de interfaces A. Goñi. Dpto. LSI, UPV/EHU Ejemplo: comprar billetes para espectáculos BASE DE DATOS Se puede implementar en una sola clase (véase PedirBilleteNO3NIVELES) en nodos cliente, dejando la base de datos en un nodo servidor A. Goñi. Dpto. LSI, UPV/EHU 7 8 public class PedirBilleteNO3NIVELES extends JFrame { // Nota: NO ESTÁ COMPLETA !! JLabel jLabel1 = new JLabel(“Nombre:”); JButton jButton1 = new JButton(“Pedir Billete”); public PedirBilleteNO3NIVELES() { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:Billetes"); } void jButton1_actionPerformed(ActionEvent e) { ResultSet rs = sentencia.executeQuery("SELECT NUMERO FROM"+ " BILLETES WHERE ESTADO=\'LIBRE\'"); if (rs.next()) { int act = sentencia.executeUpdate("UPDATE BILLETES"+ " SET ESTADO='OCUPADO', NOMBRE = "+jTextField1.getText()+ " WHERE NUMERO="+rs.getString("NUMERO")+ " AND ESTADO='LIBRE'"); if (act>0) jTextArea1.append("Asignado. \nReferencia: "+n+"\n"); else jTextArea1.append("Error al asignar billete"); }} public static void main (String []arg) { PedirBilleteNO3NIVELES b = new PedirBilleteNO3NIVELES(); A. Goñi. Dpto. LSI, UPV/EHU b.setVisible(true);}} 9 public class PedirBilleteNO3NIVELES extends JFrame { // Nota: NO ESTÁ COMPLETA !! JLabel jLabel1 = new JLabel(“Nombre:”); JButton jButton1 = new JButton(“Pedir Billete”); public PedirBilleteNO3NIVELES() { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:Billetes"); } void jButton1_actionPerformed(ActionEvent e) { ResultSet rs = sentencia.executeQuery("SELECT NUMERO FROM"+ " BILLETES WHERE ESTADO=\'LIBRE\'"); if (rs.next()) { int act = sentencia.executeUpdate("UPDATE BILLETES"+ " SET ESTADO='OCUPADO', NOMBRE = "+jTextField1.getText()+ " WHERE NUMERO="+rs.getString("NUMERO")+ " AND ESTADO='LIBRE'"); if (act>0) jTextArea1.append("Asignado. \nReferencia: "+n+"\n"); else jTextArea1.append("Error al asignar billete"); }} PRESENTACIÓN public static void main (String []arg) { PedirBilleteNO3NIVELES b = new PedirBilleteNO3NIVELES(); A. Goñi. Dpto. LSI, UPV/EHU b.setVisible(true);}} 10 public class PedirBilleteNO3NIVELES extends JFrame { // Nota: NO ESTÁ COMPLETA !! JLabel jLabel1 = new JLabel(“Nombre:”); JButton jButton1 = new JButton(“Pedir Billete”); public PedirBilleteNO3NIVELES() { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:Billetes"); } void jButton1_actionPerformed(ActionEvent e) { ACCESO A DATOS ResultSet rs = sentencia.executeQuery("SELECT NUMERO FROM"+ " BILLETES WHERE ESTADO=\'LIBRE\'"); if (rs.next()) { int act = sentencia.executeUpdate("UPDATE BILLETES"+ " SET ESTADO='OCUPADO', NOMBRE = "+jTextField1.getText()+ " WHERE NUMERO="+rs.getString("NUMERO")+ " AND ESTADO='LIBRE'"); if (act>0) jTextArea1.append("Asignado. \nReferencia: "+n+"\n"); else jTextArea1.append("Error al asignar billete"); }} public static void main (String []arg) { PedirBilleteNO3NIVELES b = new PedirBilleteNO3NIVELES(); A. Goñi. Dpto. LSI, UPV/EHU b.setVisible(true);}} LÓGICA DEL NEGOCIO public class PedirBilleteNO3NIVELES extends JFrame { // Nota: NO ESTÁ COMPLETA !! JLabel jLabel1 = new JLabel(“Nombre:”); JButton jButton1 = new JButton(“Pedir Billete”); public PedirBilleteNO3NIVELES() { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:Billetes"); } void jButton1_actionPerformed(ActionEvent e) { ResultSet rs = sentencia.executeQuery("SELECT NUMERO FROM"+ " BILLETES WHERE ESTADO=\'LIBRE\'"); if (rs.next()) { int act = sentencia.executeUpdate("UPDATE BILLETES"+ " SET ESTADO='OCUPADO', NOMBRE = "+jTextField1.getText()+ " WHERE NUMERO="+rs.getString("NUMERO")+ " AND ESTADO='LIBRE'"); if (act>0) jTextArea1.append("Asignado. \nReferencia: "+n+"\n"); else jTextArea1.append("Error al asignar billete"); }} public static void main (String []arg) { PedirBilleteNO3NIVELES b = new PedirBilleteNO3NIVELES(); A. Goñi. Dpto. LSI, UPV/EHU b.setVisible(true);}} 11 12 Arquitectura lógica del software en niveles ES MEJOR SEPARAR EL CÓDIGO EN NIVELES: USAR CLASES DISTINTAS NIVEL DE PRESENTACIÓN Nombre: Pedir Billete NIVEL DE LÓGICA DEL NEGOCIO NIVEL DE DATOS public class GB implements GestorBilletes { ... public Billete getBillete (String nom) {...} SELECT ... INSERT ... CLASES CON OPERACIONES PROPIAS DEL NEGOCIO INTERFAZ GRÁFICO DE USUARIO: Frame o Applet - inicializarSala - getBillete,... BASE DE DATOS A. Goñi. Dpto. LSI, UPV/EHU Aquí se pueden aplicar reglas del negocio: por cada 10 billetes comprados se regala uno, etc... Arquitectura lógica del software en niveles 13 • NIVEL DE PRESENTACIÓN – contiene componentes que implementan las interfaces de usuario y la interacción con el mismo – se construye mediante clases (ej: Frames, Applets), páginas HTML, JSPs,… • NIVEL DE LÓGICA DEL NEGOCIO – contiene componentes que resuelven los problemas del negocio; proporciona servicios u operaciones que implementan las reglas propias del negocio. – se construye mediante clases Java con los servicios/operaciones de la lógica del negocio • Se consigue más extensibilidad si la lógica del negocio se define mediante interfaces Java. • NIVEL DE DATOS – utilizado por el nivel lógica del negocio para conseguir la persistencia de datos. Es una o más BDs. A. Goñi. Dpto. LSI, UPV/EHU 14 Arquitectura lógica del software en niveles • Ventaja: AISLAR UN NIVEL DE LOS OTROS – Se puede cambiar el nivel de presentación minimizando los cambios en los niveles de lógica del negocio y de datos – Se puede cambiar alguna de las reglas del negocio con pocos cambios en el resto – Se puede cambiar la base de datos con pocos cambios en el resto UNA ARQUITECTURA LÓGICA DEL SOFTWARE EN VARIOS NIVELES FAVORECE LA EXTENSIBILIDAD Y REUTILIZACIÓN DEL SOFTWARE A. Goñi. Dpto. LSI, UPV/EHU 15 El análisis y diseño de los casos de uso seguía una filosofía en varios niveles… A. Goñi. Dpto. LSI, UPV/EHU Los diagramas de colaboración (análisis) del PUD siguen filosofía 3 niveles NIVEL DE PRESENTACIÓN NIVEL DE DATOS NIVEL DE LÓGICA DEL NEGOCIO A. Goñi. Dpto. LSI, UPV/EHU 16 17 Los diagramas de colaboración (análisis) del PUD siguen filosofía 3 niveles NIVEL DE PRESENTACIÓN • Clases FRONTERA representan • Clases CONTROL NIVEL DE LÓGICA DEL NEGOCIO representan NIVEL DE DATOS • Clases ENTIDAD representan A. Goñi. Dpto. LSI, UPV/EHU Y también los de secuencia (diseño) 18 Lógica del Negocio : Usuario : PedirBillete : GestorBilletes : GestorBD Proporcionar nombre y solicitar billete Datos getBillete(nombre) executeQuery:=executeQuery( P1) : ResultSet new() Presentación next() next() [hay libres] : get("NUMERO") num executeUpdate:=executeUpdate(P2) b : Billete new(num, nom) [no hay libres]: new(-1, "") b A. Goñi. Dpto. LSI, UPV/EHU Arquitectura física del software en niveles 19 • La separación FÍSICA de los niveles lógicos admite distintas posibilidades: • Arquitectura en 2 niveles físicos – 2 de los niveles se juntan en un nodo – 2 posibilidades: • la lógica del negocio se junta con la presentación • parte de la lógica del negocio se junta con los datos • Arquitectura en 3 niveles físicos – Cada nivel lógico en un nodo distinto • Aplicaciones de más niveles físicos – nivel de presentación instalado en el servidor Web y ejecutado en cliente (navegador Web) A. Goñi. Dpto. LSI, UPV/EHU 20 Arquitectura física en 2 niveles: cliente gordo/servidor flaco • El nivel de presentación y el de la lógica del negocio se unen en un nodo. En el otro queda el nivel de datos. CLIENTE NIVEL DE PRESENTACIÓN NIVEL DE LÓGICA DEL NEGOCIO SERVIDOR NIVEL DE DATOS • Comunicación entre Cliente y Servidor en SQL • Se necesitan APIs como por ejemplo JDBC y/o ODBC • Deben instalarse DRIVERS de la BD en todos los clientes A. Goñi. Dpto. LSI, UPV/EHU 21 CLIENTE public class PedirBillete2NivCliGordo extends JFrame { GestorBilletes2NivCliGordo gestorBilletes; Presentación void jButton1_actionPerformed(ActionEvent e) { int res = gestorBilletes.getBillete(jTextField1.getText()).getNum(); if (res<0) jTextArea1.append("Error al asignar billete"); else jTextArea1.append("Asignado. \nReferencia: "+res+"\n");} } Lógica del Negocio public class GestorBilletesBD implements GestorBilletes2NivCliGordo { public GestorBilletesBD() { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:Billetes");} public Billete getBillete(String nom) {ResultSet rs = sent.executeQuery("SELECT NUMERO…";); int act = sent.executeUpdate("UPDATE BILLETES …“); if (act>0) return new Billete(n,nom); // Núm. billete asignado else return new Billete(-1,"");; } // No había ninguno libre}}} INSTALAR LA CLASE sun.jdbc.odbc.JdbcOdbcDriver Y DEFINIR FUENTE DATOS ODBC “Billetes” A. Goñi. Dpto. LSI, UPV/EHU BD Datos SERVIDOR 22 CLIENTE Sentencia SQL CLIENTE Respuesta CLIENTE CLIENTE BD Driver CLIENTE CLIENTE dbcDriver CLIENTE OdbcDriver CLIENTE CLIENTE c.JdbcOdbcDriver CLIENTE CLIENTE bc.JdbcOdbcDriver SERVIDOR CLIENTE bc.odbc.JdbcOdbcDriver CLIENTE CLIENTE n.jdbc.odbc.JdbcOdbcDriver CLIENTE ¡¡ CADA CLIENTE CLIENTE sun.jdbc.odbc.JdbcOdbcDriver sun.jdbc.odbc.JdbcOdbcDriver CLIENTE sun.jdbc.odbc.JdbcOdbcDriver sun.jdbc.odbc.JdbcOdbcDriver A. Goñi. Dpto. LSI, UPV/EHU UNA CONEXIÓN ABIERTA CON LA BD!! 23 Arquitectura física en 2 niveles: cliente gordo/servidor flaco • El despliegue de la aplicación es alto: instalar drivers y configurar todos los clientes • Cambiar de SGBD requiere reinstalar todos los clientes • Cambiar el esquema de la BD puede afectar a los clientes • Cambiar la lógica del negocio implica recompilar y desplegar en todos los clientes • Costos de conexión con la BD son altos. Cada cliente una conexión. • La red se puede sobrecargar. Cada sentencia SQL usa la red. A. Goñi. Dpto. LSI, UPV/EHU Arquitectura física en 2 niveles: cliente flaco/servidor gordo • Parte de la lógica del negocio se combina con el nivel de datos CLIENTE NIVEL DE PRESENTACIÓN NIVEL DE LÓGICA DEL NEGOCIO 1 SERVIDOR NIVEL DE DATOS + LÓGICA DEL NEGOCIO 2 • Se usan procedimientos almacenados (stored procedures) en la BD. Un procedimiento almacenado puede servir para ejecutar una serie de sentencias SQL. • Comunicación Cliente/Servidor en SQL + Proc. almacenados • Se necesitan APIs como por ejemplo JDBC y/o ODBC • Deben instalarse DRIVERS de la BD en todos los clientes A. Goñi. Dpto. LSI, UPV/EHU 24 25 Arquitectura física en 3 niveles CLIENTE CLIENTE SERVIDOR CLIENTE SERVIDOR Nivel de Presentación Nivel de Lógica del Negocio (en SERVIDORES DE APLICACIONES) Nivel de Datos BASE DE DATOS A. Goñi. Dpto. LSI, UPV/EHU 26 CLIENTE public class PedirBillete extends JFrame { GestorBilletes gestorBilletes; Presentación void jButton1_actionPerformed(ActionEvent e) { int res = gestorBilletes.getBillete(jTextField1.getText()).getNum(); if (res<0) jTextArea1.append("Error al asignar billete"); else jTextArea1.append("Asignado. \nReferencia: "+res+"\n");} } BD Datos SERVIDOR DATOS SERVIDOR APLICACIONES INSTALAR LA CLASE sun.jdbc.odbc. JdbcOdbcDriver Y DEFINIR FUENTE DATOS ODBC “Billetes” A. Goñi. Dpto. LSI, UPV/EHU public class ServidorGestorBilletesBD Lógica del Negocio implements GestorBilletes { public ServidorGestorBilletesBD() { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:Billetes");} public Billete getBillete(String nom) {ResultSet rs = sent.executeQuery("SELECT NUMERO…";); int act = sent.executeUpdate("UPDATE BILLETES …“); if (act>0) return new Billete(n,nom); // Núm. billete asignado else return new Billete(-1,"");; } // No había ninguno libre}}} 27 BD sun.jdbc.odbc.JdbcOdbcDriver SERVIDOR CLIENTE APLICACIONES CLIENTE CLIENTE sun.jdbc.odbc.JdbcOdbcDriver SERVIDOR SERVIDOR CLIENTE DATOS APLICACIONES CLIENTE CLIENTE CLIENTE CLIENTE SERVIDOR CLIENTE APLICACIONES CLIENTE sun.jdbc.odbc.JdbcOdbcDriver CLIENTE CLIENTE CLIENTE ¡¡ SÓLO LOS SERVIDORES CLIENTE CLIENTE DE APLICACIONES TIENEN CLIENTE CONEXIONES ABIERTAS CLIENTE CON LA BD!! A. Goñi. Dpto. LSI, UPV/EHU 28 Arquitectura física en 3 niveles • Sólo hay que instalar los drivers de la BD en los nodos donde se encuentre la lógica del negocio (nodos servidores) • Cambiar de SGBD/esquema de la BD NO requiere reinstalar todos los clientes. Sólo los de la lógica del negocio. • Cambiar la lógica del negocio NO implica recompilar y desplegar en todos los clientes. • Costos de conexión con la BD NO son tan altos. Los clientes no realizan conexiones con la BD. Sólo los servidores con la lógica del negocio lo hacen. En general, se MEJORA en EFICIENCIA, MANTENIMIENTO y EXTENSIBILIDAD A. Goñi. Dpto. LSI, UPV/EHU Las aplicaciones Web permiten más niveles CLIENTE (Nav. Web) CLIENTE (Nav. Web) SERVIDOR WEB SERVIDOR WEB SERVIDOR APLICACIONES CLIENTE (Nav. Web) CLIENTE (Nav. Web) SERVIDOR WEB SERVIDOR APLICACIONES A. Goñi. Dpto. LSI, UPV/EHU BASE DE DATOS Nivel de Presentación (ejecución) Nivel de Presentación (instalación) Nivel de Lógica del Negocio Nivel de Datos 29 30 CLIENTE Navegador Web Ejecutar Presentación SERVIDOR WEB BD Datos HTML + JSP + Bean Instalar Presentación SERVIDOR DATOS SERVIDOR APLICACIONES INSTALAR LA CLASE sun.jdbc.odbc. JdbcOdbcDriver Y DEFINIR FUENTE DATOS ODBC “Billetes” A. Goñi. Dpto. LSI, UPV/EHU public class ServidorGestorBilletesBD Lógica del Negocio implements GestorBilletes { public ServidorGestorBilletesBD() { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:Billetes");} public Billete getBillete(String nom) {ResultSet rs = sent.executeQuery("SELECT NUMERO…";); int act = sent.executeUpdate("UPDATE BILLETES …“); if (act>0) return new Billete(n,nom); // Núm. billete asignado else return new Billete(-1,"");; } // No había ninguno libre}}} BD sun.jdbc.odbc.JdbcOdbcDriver SERVIDOR APLICACIONES sun.jdbc.odbc.JdbcOdbcDriver SERVIDOR APLICACIONES SERVIDOR WEB CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE SERVIDOR WEB CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE CLIENTE A. Goñi. Dpto. LSI, UPV/EHU SERVIDOR APLICACIONES SERVIDOR sun.jdbc.odbc.JdbcOdbcDriver DATOS EN LOS CLIENTES NO HAY QUE INSTALAR NADA (EXCEPTO NAVEGADOR WEB+ INTERNET) 31 32 Arquitectura física en 3 niveles • Existe tecnología que permite construir aplicaciones siguiendo esta filosofía de componentes y objetos distribuidos (server-side components) – Entreprise JavaBeans (EJBs) es la arquitectura de componentes para la plataforma Java 2 Enterprise Edition (J2EE). Definido por Sun Microsystems • El nivel de presentación se puede dividir más usando Java Applets, Servlets y/o JSPs – CORBA es una arquitectura para comunicación entre objetos distribuidos a través de ORBs (Object Request Brokers). Es un estándar definido por OMG. – DCOM/COM+ y la plataforma .NET es la tecnología equivalente desarrollada por Microsoft A. Goñi. Dpto. LSI, UPV/EHU 33 Arquitectura física en 3 niveles • Pero también se puede conseguir con tecnología “sencilla” de Java: – Construcción de interfaces gráficas en Java (AWT y SWING) para definir el NIVEL DE PRESENTACIÓN – Ejecución del NIVEL DE PRESENTACIÓN EN UN navegador WEB (Págs. con Applets o Págs. JSPs) – Computación con objetos distribuidos (RMI) para definir el NIVEL LÓGICA DEL NEGOCIO e invocarlo desde el NIVEL DE PRESENTACIÓN – Llamadas a JSPs y de ellos a JavaBeans – Conexión con BDs relacionales (JDBC) para conseguir la comunicación entre NIVEL LÓGICA DEL NEGOCIO y DATOS A. Goñi. Dpto. LSI, UPV/EHU Preguntas: ¿qué cambios habría que realizar si…? • Se quisiera cambiar el SGBD de Access a MySQL • Si se quisiera cambiar la BD: por ejemplo tabla BILLETES => ENTRADAS • Quisiéramos una nueva regla del negocio: no permitir comprar más de 6 entradas a la misma persona • Se desea que la respuesta salga en otra ventana A. Goñi. Dpto. LSI, UPV/EHU 34 35 2. Construcción de interfaces gráficas en Java AWT y Swing A. Goñi. Dpto. LSI, UPV/EHU 36 Índice • Introducción – Objetivos – La jerarquía de clases: patrón de diseño Composite • Componentes principales – Contenedores de AWT/Swing – Componentes de AWT/Swing – Administradores de diseño • Gestión de Eventos – Eventos de bajo y alto nivel – Listeners – Adapters • Separación entre Nivel de Presentación y Lógica del Negocio usando interfaces Java. A. Goñi. Dpto. LSI, UPV/EHU 37 Introducción • Es interesante disponer de herramientas de ayuda para la construcción de interfaces gráficas de usuario (GUI). • Java proporciona clases para conseguir: – Diseño y programación de interfaces fácil y rápido. – Diseño de Applets para la Web. • En concreto, Java proporciona – clases AWT (Abstract Windowing Toolkit) – clases Swing • Swing es posterior a AWT y tiene más componentes A. Goñi. Dpto. LSI, UPV/EHU 38 Introducción • Objetivos – Entender la jerarquía de clases diseñada en Java que permite construir interfaces de usuario – Entender cómo se realiza la gestión de eventos – NO es objetivo aprender los nombres de todas las clases, listeners... Al fin y al cabo se pueden construir interfaces de usuario usando herramientas de desarrollo Java (ej: JDeveloper) A. Goñi. Dpto. LSI, UPV/EHU 39 AWT. Componentes principales • Jerarquía de clases de AWT: COMPONENTE Applet CONTENEDOR (todo contenedor es un componente) A. Goñi. Dpto. LSI, UPV/EHU 40 La jerarquía de clases de AWT sigue un patrón de diseño COMPOSITE Componente ComConc1 ComConcN Clase Contenedor add (Componente c) -- el método add añade -- componentes -- al contenedor A. Goñi. Dpto. LSI, UPV/EHU Contenedor ContConc1 ContConcM Un contenedor se compone de varios componentes, los cuales pueden ser componentes concretos o pueden ser contenedores. Estos contenedores pueden estar a su vez compuestos de varios componentes. 41 La jerarquía de clases de AWT sigue un patrón de diseño COMPOSITE COMPONENTE Applet Las posibilidades son infinitas… CONTENEDOR (todo contenedor es un componente) - Una ventana formada por 2 cajas de texto, 2 campos de texto, 3 botones, 1 panel que contenga 5 casillas de validación y una lista desplegable. -Una ventana formada por 2 etiquetas, 2 campos de texto y un botón -… Además, añadir nuevos tipos de contenedores y de componentes no sería muy costoso (estaríamos ante una solución extensible) A. Goñi. Dpto. LSI, UPV/EHU 42 Un diseño de clases como este sería francamente malo … Button * * CheckBox TextField * Frame * … - Se necesitarían métodos addButton, addCheckBox, addTextField, addFrame en la clase Frame (y los correspondientes a todos los que faltan) - Además el diagrama está muy incompleto, ya que un Button puede ser componente de un Panel, Dialog, … - Si se quisiera añadir un nuevo componente XXX, habría que cambiar la clase Frame y añadir el método addXXX (nada extensible) A. Goñi. Dpto. LSI, UPV/EHU 43 Componentes principales de Swing Applet JWindow JDialog JApplet A. Goñi. Dpto. LSI, UPV/EHU JComponent JFrame JPanel JList JComboBox JMenuBar JTextField, JTextArea, JButton… 44 Contenedores • El contenedor más importante es el Frame/JFrame, pero también hay otros como Panel/JPanel y Dialog/JDialog. • Clases Frame/JFrame – Ventana lisa a la que se le puede añadir título y ofrece los iconos para maximizar, minimizar y destruir. – Es el único contenedor al que se le pueden insertar menús • Clases Panel/JPanel – Son los contenedores genéricos que sirven para agrupar componentes. Es la clase que se utiliza para insertar contenedores dentro de otros contenedores (subpaneles dentro de paneles) – La clase JFrame de Swing incluye de manera automática un Panel. La clase Frame de AWT NO. A. Goñi. Dpto. LSI, UPV/EHU 45 Para acceder al JPanel asociado al JFrame A. Goñi. Dpto. LSI, UPV/EHU 46 jPanel4 jComboBox1 jRadioButton1 jPanel2 jRadioButton2 jPanel3 jTextArea1 jButton1 jPanel1 A. Goñi. Dpto. LSI, UPV/EHU Componentes principales de AWT / Swing 47 • Clases TextField / JTextField – área de entrada de texto de una línea • Clases TextArea / JTextArea – permite introducir varias líneas de texto – también puede usarse para mostrar texto – TextArea implementa un scroll vertical y horizontal por cada área de texto – JTextArea no añade automáticamente un scroll. Hay que añadir el JTextArea a un JScrollPane A. Goñi. Dpto. LSI, UPV/EHU 48 JDeveloper genera el código añadiendo el método jbInit y capturando cualquier posible excepción. No lo pondremos así en los siguientes ejemplos por ahorrar espacio. A. Goñi. Dpto. LSI, UPV/EHU Componentes principales de AWT / Swing • Clases Button / JButton – Creación de botones. Un botón nos permitirá realizar algún tipo de acción cuando sea pulsado. • Clases Label / JLabel – Pueden mostrar información – Se suelen utilizar junto con los campos de texto. A. Goñi. Dpto. LSI, UPV/EHU 49 50 JFrame A. Goñi. Dpto. LSI, UPV/EHU Componentes principales de AWT / Swing • Clases Checkbox/JCheckBox/JRadioButton – Las casillas de verificación permiten ofrecer al usuario la posibilidad de activar/desactivar opciones. – Los JRadioButton pueden añadirse a ButtonGroup si se debe ESCOGER SÓLO UNA OPCIÓN – En AWT puede conseguirse el mismo resultado si los Checkbox se definen dentro de un mismo Checkboxgroup A. Goñi. Dpto. LSI, UPV/EHU 51 52 A. Goñi. Dpto. LSI, UPV/EHU Componentes principales de AWT / Swing • Clases List / JList – Las listas desplazables permiten introducir una serie de opciones que después el usuario puede elegir. En ocasiones son utilizadas para evitar la saturación de componentes en la pantalla. – El List de AWT incluye un scroll que aparece cuando los elementos en la lista no caben en el espacio asignado al List. – El JList no. Hay que añadirlo a un JScrollPane. A. Goñi. Dpto. LSI, UPV/EHU 53 54 Pero sólo si se hace antes de que se muestre el JFrame b.elementos.addElement(“C++”); aquí no funcionaría Con vector no funciona si la lista es “dinámica” A. Goñi. Dpto. LSI, UPV/EHU 55 Por ejemplo, javax.Swing ofrece la clase DefaultListModel, que proporciona los mismos métodos que Vector Para listas “dinámicas” usar un ListModel A. Goñi. Dpto. LSI, UPV/EHU 56 A. Goñi. Dpto. LSI, UPV/EHU Componentes principales de AWT / Swing 57 • Clases Choice / JComboBox – Esta clase se utiliza para crear menús de opciones. – Su ventaja es el poco espacio que ocupan los menus desplegables en pantalla. A. Goñi. Dpto. LSI, UPV/EHU 58 A. Goñi. Dpto. LSI, UPV/EHU JTable JTable para mostrar y editar tablas de datos Se puede redimensionar… Modificar valores en celdas… A. Goñi. Dpto. LSI, UPV/EHU 59 Componentes principales de AWT / Swing • Creación de menús en Swing – La clase JFrame es el único contenedor que puede alojar a una barra de menús. – La clase JMenuBar permite crear la barra de menús sobre la que se insertarán los menús. – La clase JMenu permite crear menús a los que se les da un nombre y que muestran “elementos” en una ventana desplegable – Los “elementos” del menú pueden ser objetos de JMenuItem o de JMenu (para menús en cascada). • En AWT existen otras clases similares A. Goñi. Dpto. LSI, UPV/EHU 60 61 Menús en Swing JMenu JMenu JMenuBar JMenuItem A. Goñi. Dpto. LSI, UPV/EHU JMenuItem 62 JDeveloper ayuda un poco… A. Goñi. Dpto. LSI, UPV/EHU 63 Menús en AWT Objeto de MenuBar Objeto de MenuItem Objeto de Menu Objeto de MenuShort Objeto de Menu Objeto de CheckboxMenuItem A. Goñi. Dpto. LSI, UPV/EHU 64 A. Goñi. Dpto. LSI, UPV/EHU 65 Administradores de diseño: Layout • Sirven para decidir dónde se coloca un componente dentro del contenedor contenedor.add(componente); • FlowLayout (administrador por defecto para Panel) – va añadiendo los componentes en una línea hasta que se termina y pasa a la siguiente • BorderLayout (administrador de Frame y Dialog ) – añade los componentes en 5 áreas: norte, sur, oeste, este y centro • El administrador por defecto se puede cambiar: contenedor.setLayout(new BorderLayout()); A. Goñi. Dpto. LSI, UPV/EHU Administradores de diseño: Layout • Si se desea colocar el componente en unas determinadas coordenadas, entonces hay que quitar el administrador de diseño componente.setLayout(null); // o setLayout(new XYLayout()); • Suponiendo que this es un contenedor y textField1 un componente suyo setLayout(null); textField1.setBounds(15,20,50,60); lo coloca en: x A. Goñi. Dpto. LSI, UPV/EHU y anchura altura 66 67 Label en BorderLayout.NORTH Panel con BorderLayout Panel con GridLayout(4,3) situado en BorderLayout.CENTER Panel con FlowLayout situado en BorderLayout.SOUTH A. Goñi. Dpto. LSI, UPV/EHU 68 jPanel1 jPanel2 A. Goñi. Dpto. LSI, UPV/EHU jPanel4 69 Ventajas al definirlo con administradores de diseño: se redibujan los componentes automáticamente al cambiar el tamaño de la ventana. A. Goñi. Dpto. LSI, UPV/EHU 70 Sin administrador de diseño. Se dan las coordenadas de todos los componentes (usando una herramienta visual es fácil) A. Goñi. Dpto. LSI, UPV/EHU 71 Como desventaja clara, es que al redimensionar el Frame, los componentes se quedan donde estaban. Se podría programar el evento asociado a redimensionar la ventana y recalcular las coordenadas… A. Goñi. Dpto. LSI, UPV/EHU 72 Otros contenedores • Clases Dialog/JDialog – Ventana que permite leer datos del usuario – Puede asignársele la característica de ser MODAL, para no permitir cambiar a otra ventana mientras esté activa. • Clase FileDialog (AWT) / JFileChooser (Swing) – Ventana que permite abrir/guardar ficheros (modos FileDialog.LOAD y FileDialog.SAVE) – En AWT, se utiliza la misma ventana de diálogo del Sistema Operativo en el que se está ejecutando la máquina virtual Java – Ofrece ya cierta funcionalidad. No hay que reprogramar el caso en el que se intenta sobreescribir un fichero (muestra ventana de alerta) A. Goñi. Dpto. LSI, UPV/EHU 73 Ventana MODAL: hasta que no se cierre no puede cambiarse de ventana modal a true A. Goñi. Dpto. LSI, UPV/EHU 74 Necesario asociar el FileDialog a un Frame “padre”, pero no es necesario llamar a getDirectory/File desde él A. Goñi. Dpto. LSI, UPV/EHU 75 Gestión de eventos • Al diseñar una interfaz gráfica hay que tener en cuenta que se producirán ciertos EVENTOS como consecuencia de las acciones del usuario. • Por lo tanto, el programador tendrá que preparar una serie de ACCIONES para procesar los eventos del usuario. A. Goñi. Dpto. LSI, UPV/EHU 76 Eventos • Evento es un suceso – generado por una acción del usuario – que afecta a algún componente de la interfaz • Ejemplos de eventos: – pulsar una tecla, mover el ratón, cambiar el formato de una ventana, cerrar una venta, minimizar una ventana, pulsar un botón, se pierde (o gana) el foco en un componente, se cambia el valor de un área de texto, seleccionar un item en el menú, etc. A. Goñi. Dpto. LSI, UPV/EHU 77 Tipos de eventos • Eventos de bajo nivel – Relacionados con aspectos físicos de la interfaz de usuario. Ejemplos: pulsación de teclas, movimientos de ratón, hacer click, ganar/perder el foco en un componente, abrir/cerrar ventana.. • Eventos de alto nivel o semánticos – Tienen que ver con la semántica de los componentes. Ejemplos: pulsar un botón, cambiar el texto de un campo, seleccionar un item en un menú/lista/choice – Generalmente son combinaciones de eventos de bajo nivel. A. Goñi. Dpto. LSI, UPV/EHU 78 Los eventos son objetos Los eventos de bajo nivel son objetos de clases • ContainerEvent – se producen cuando se añaden/eliminan componentes • FocusEvent – se generan cuando el componente ha ganado o perdido el enfoque (foco) del teclado • InputEvent (con subclases KeyEvent y MouseEvent) – se generan cuando el usuario pulsa una tecla, mueve el ratón o hace click con él • WindowEvent – se generan cuando se usa alguno de los controles de ventana (minimizar, cerrar, …) Estas clases son subclases de java.awt.AWTEvent A. Goñi. Dpto. LSI, UPV/EHU 79 Los eventos son objetos Los eventos de alto nivel son objetos de clases • ActionEvent – se producen cuando se realizan acciones específicas sobre componentes: ej: pulsaciones de botones • AdjustmentEvent – se generan cuando una barra de desplazamiento cambia • ItemEvent – se generan cuando el usuario cambia una elección, una lista o casilla de verificación (Choice,List,CheckBox) • TextEvent – se generan cuando cambia el texto en un componente TextArea o TextField Estas clases son subclases de java.awt.AWTEvent A. Goñi. Dpto. LSI, UPV/EHU 80 Interfaces Listener • Para gestionar eventos, Java proporciona unas interfaces “oyentes” (Listeners), donde cada una de ellas contiene métodos que hay que implementar. • La implementación proporcionada para cada método es la respuesta apropiada a cada evento. • Si sobre un objeto gráfico queremos controlar cierto tipo de eventos, le asignaremos un “listener” objGraf.addXXXListener(objListener) A. Goñi. Dpto. LSI, UPV/EHU 81 obj1.addXXXListener(obj2); obj1 contiene una referencia a un OBJETO GRÁFICO (botón, ventana, lista desplegable, checkbox, etc) sobre el que se quiere definir un comportamiento ante EVENTOS En tiempo de ejecución sucede un EVENTO que afecta al objeto de obj1 A. Goñi. Dpto. LSI, UPV/EHU obj2 contiene una referencia a un objeto de una clase que implementa el interface XXXListener. public interface XXXListener { .... void accionYYY (AWTEvent e); } Se crea un objeto evento(AWTEvent) y se pasa el control al objeto oyente (el de obj2) El objeto oyente ejecutará el método correspondiente a la acción (Ej.: accionYYY) usando el objeto Evento generado como parámetro 82 Tras pulsar el botón OyenteBoton es INNER CLASS (clase definida dentro de otra). Por eso se puede acceder a los atributos jPanel1, jButton1,… A. Goñi. Dpto. LSI, UPV/EHU Asociar componentes gráficos con Listeners • A cada componente gráfico tenemos que decirle qué interfaz Listener queremos implementar: addXXXListener() • Por ejemplo: – addActionListener(ActionListener oyente) – addItemListener(ItemListener oyente) • NO ES NECESARIO aprenderse los distintos tipos de Listeners, si se usa una herramienta visual de desarrollo. A. Goñi. Dpto. LSI, UPV/EHU 83 84 1) Hacer click en el componente 2) Seleccionar el Evento (actionPerformed) y hacer click 3) Escribir el código donde indica JDeveloper A. Goñi. Dpto. LSI, UPV/EHU 85 Clases Adapter • Algunos Listeners tienen varios métodos, pero tal vez no queramos implementarlos todos (porque no queremos responder a todos los tipos de eventos posibles). • Sin embargo, Java obliga a implementar todos los métodos de una interfaz – Conclusión: muchas veces se proporcionarán implementaciones vacías a los métodos. • Por eso, Java ofrece al programador unas clases adaptadoras (Adapter) que implementan todos los métodos de las interfaces (dejándolos vacíos) • Usando una herramienta visual NO es problema A. Goñi. Dpto. LSI, UPV/EHU JDeveloper genera las clases Oyente como INNER CLASS sin nombre Tras pulsar el botón A. Goñi. Dpto. LSI, UPV/EHU 86 87 Definición de una clase SIN NOMBRE que implementa el interface ActionListener button1.addActionListener(new ESTE CÓDIGO SE GENERA DE MANERA AUTOMÁTICA CON JDeveloper java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { button1_actionPerformed(e); } }); LA ACCIÓN HAY QUE PROGRAMARLA, CLARO void button1_actionPerformed(ActionEvent e) { textArea1.setText("Opción: "+checkboxGroup1.getCurrent().getLabel()); } A. Goñi. Dpto. LSI, UPV/EHU 88 A. Goñi. Dpto. LSI, UPV/EHU 89 Se puede usar un único método para tratar los eventos de los dos JRadioButton. El texto se puede obtener del objeto EVENTO !! A. Goñi. Dpto. LSI, UPV/EHU 90 Se puede usar un único método para tratar los eventos de los dos JRadioButton. El texto se puede obtener del objeto EVENTO (ActionEvent e) Para obtener información de contexto del evento (como la etiqueta del componente gráfico sobre el que se ha producido el evento) A. Goñi. Dpto. LSI, UPV/EHU Cuando entra el ratón en el botón91 Inicialmente… Punto donde se encuentra el ratón Al hacer “drag” dentro del botón A. Goñi. Dpto. LSI, UPV/EHU Al salir el ratón del botón 92 A. Goñi. Dpto. LSI, UPV/EHU 93 jTextField1 Cada vez que se escribe un carácter en la caja de texto, se añade al área de texto y se borra de la caja de texto. Cada 25 caracteres se salta de línea en el área de texto. A. Goñi. Dpto. LSI, UPV/EHU jTextArea1 94 Separación entre Nivel de Presentación y Lógica del Negocio • Es conveniente separar el nivel de presentación del de la lógica del negocio • El nivel de presentación está formado por las clases de AWT/Swing • La llamada al nivel lógica del negocio se realizará en algún método de respuesta a un evento. • Se puede incluir un atributo que contenga el objeto con la lógica del negocio (DE TIPO interface JAVA) – Podría conseguirse cambiar la lógica del negocio SIN NECESIDAD DE cambiar el nivel de presentación. Incluso sin RECOMPILAR, e incluso haciéndolo EN TIEMPO DE EJECUCIÓN (sin relanzar el objeto de presentación) A. Goñi. Dpto. LSI, UPV/EHU 95 Interface LogicaNegocio hacerX(… ) hacerY(… ) usa Clase Presentacion logNe: LogicaNegocio setLogicaNegocio (l: LogicaNegocio) En alguno de los métodos de la clase Presentación (los que responden a eventos) se usará: logNe.hacerX(…) o bien logNe.hacerY(…) A. Goñi. Dpto. LSI, UPV/EHU Interface LogicaNegocio 96 hacerX(… ) hacerY(… ) usa Clase Presentacion logNe: LogicaNegocio clase LogicaNegocioConcreta setLogicaNegocio (l: LogicaNegocio) hacerX(… ) // Implementada hacerY(… ) // Implementada PARA CREAR LA INTERFAZ GRÁFICA CON SU LÓGICA DEL NEGOCIO: Presentacion p = new Presentacion(); p.setLogicaNegocio(new LogicaNegocioConcreta()); p.setVisible(true); A. Goñi. Dpto. LSI, UPV/EHU 97 Interface LogicaNegocio Clase Presentacion usa logNe: LogicaNegocio setLogicaNegocio (l: LogicaNegocio) hacerX(… ) hacerY(… ) clase LogicaNegocionNueva hacerX(… ) // Implementada hacerY(… ) // Implementada Si ahora se quisiera cambiar la lógica del negocio, bastaría con hacer: p.setLogicaNegocio(new LogicaNegocioNueva()); NO HACE FALTA RECOMPILAR LA CLASE Presentacion, y, si se conoce la referencia del objeto, SE PUEDE CAMBIAR LA LÓGICA DEL NEGOCIO EN TIEMPO DE EJECUCIÓN A. Goñi. Dpto. LSI, UPV/EHU 98 A. Goñi. Dpto. LSI, UPV/EHU 99 Sólo falta proporcionar la clase que implemente GestorBilletes… (con o sin nombre) A. Goñi. Dpto. LSI, UPV/EHU 100 Interface GestorBilletes Clase PedirBillete usa gestorBilletes: GestorBilletes setGestorBilletes (GestorBilletes) getBillete(String):int clase GestorBilletesImp getBillete(String):int // Implementada A. Goñi. Dpto. LSI, UPV/EHU 101 3. Conexión con BDs relacionales en Java JDBC A. Goñi. Dpto. LSI, UPV/EHU 102 Índice • Introducción • Arquitectura para JDBC. Tipos de drivers • Las clases e interfaces de JDBC • Ejemplo de uso de JDBC: un servidor de billetes • Conexión con el nivel de presentación A. Goñi. Dpto. LSI, UPV/EHU 103 Introducción • JDBC (Java Database Connectivity) es un API; un conjunto de clases y métodos que se encuentran en el paquete java.sql y que sirven para – establecer conexiones con bases de datos – enviar sentencias SQL a dichas BDs – procesar los resultados • Los vendedores de SGBD deben escribir “drivers”o controladores para JDBC, o al menos, permitir que programas que usan el API JDBC puedan conectarse con sus BDs. Hay muchos: http://industry.java.sun.com/products/jdbc/drivers A. Goñi. Dpto. LSI, UPV/EHU 104 JDBC • JDBC ofrece igual funcionalidad que ODBC (Open Database Connectivity) de Microsoft – El problema es que ODBC tiene interfaz C • La gran mayoría de SGBD disponen ya de drivers para trabajar con ODBC • En el JDK se suministra un puente JDBCODBC para convertir llamadas JDBC a ODBC y poder acceder así a BDs que ya tienen un driver ODBC mediante JDBC – En el caso de hacerlo así, hay que registrar la BD correspondiente para ser usada con ODBC A. Goñi. Dpto. LSI, UPV/EHU 105 Arquitectura para JDBC: 4 tipos de drivers Aplicación JAVA API de JDBC JDBC Driver Manager API de JDBC Driver Driver puente JDBC-ODBC Driver ODBC SGBD A. Goñi. Dpto. LSI, UPV/EHU Distintos tipos de Driver JDBC: Driver JDBC A) Traduce a APIs nativas del SGBD. B) Se comunica con aplicación intermedia en el servidor que traslada peticiones al SGBD SGBD que se trate C) Se comunica directamente con el SGBD 106 APLICACIÓN Connection c; Cargar Driver SGBD X Abrir Conexión BD 1 Crear Statement Enviar Sentencia SQL BD 1 Enviar/ Recibir SQL BD 2 Statement s SGBD X A. Goñi. Dpto. LSI, UPV/EHU 107 Cargar el Controlador/Driver • Para cargar un controlador se puede usar el método forName() de la clase Class (carga clases Java) Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”); – carga el puente JDBC-ODBC Class.forName(“org.gjt.mm.mysql.Driver”); – carga el Driver JDBC para trabajar con el SGBD mySQL • La clase java.lang.Class permite crear instancias de clases en tiempo de ejecución: Object o = Class.forName(“org.gjt.mm.mysql.Driver”).newInstance(); – Crea una instancia de la clase org.gjt.mm.mm.mysql.Driver y la asigna al objeto o A. Goñi. Dpto. LSI, UPV/EHU 108 La clase DriveManager • Sirve para registrar los controladores o drivers DriverManager.registerDriver( new sun.jdbc.odbc.JdbcOdbcDriver()); • No es necesario hacerlo si se ha cargado el Driver con Class.forName • Además puede establecer conexiones con BDs Connection c = DriverManager.getConnection( String dir,String usu,String clave) – dir identifica a la BD. Dependiendo del SGBD usado tendrá una forma u otra jdbc:subprotocolo//servidor:puerto/base de datos – usu y clave son nombre de usuario y clave, si hay A. Goñi. Dpto. LSI, UPV/EHU 109 La interfaz Connection • Una vez que tenemos una conexión abierta, se pueden lanzar sentencias SQL desde el programa Java. • Para ello hay que crear objetos “Statement” Statement s = c.createStatement(); // c contiene un objeto de Connection • Las conexiones hay que cerrarlas cuando no se necesitan (no queremos enviar más sentencias SQL a la BD) para liberar recursos c.close(); A. Goñi. Dpto. LSI, UPV/EHU 110 La interfaz Connection • Por defecto, toda sentencia SQL enviada a un Statement termina con un commit si tiene éxito. • Si se quiere que varias sentencias SQL formen una transacción, se puede hacer así: c.setAutoCommit(false); // Enviar las sentencias SQL que forman // la transacción Ejecutar c.commit(); o bien c.rollback(); A. Goñi. Dpto. LSI, UPV/EHU 111 Interfaz Statement • Sirve para enviar preguntas SQL a la BD int i = s.executeUpdate(String sql); // s contiene un objeto de Statement ejecuta una sentencia SQL INSERT, UPDATE o DELETE y devuelve el número de tuplas afectadas ResultSet r = s.executeQuery(String sql); ejecuta una sentencia SQL SELECT y devuelve el resultado en un objeto ResultSet NOTA: sólo puede haber un ResultSet “abierto” sobre un objeto “Statement” CONCLUSIÓN: si queremos trabajar con dos preguntas SQL a la vez se necesita definir dos statement diferentes A. Goñi. Dpto. LSI, UPV/EHU 112 Interfaz Statement • Se puede cerrar un statement, para liberar recursos: s.close(); // s contiene un objeto de Statement • Se puede limitar el número máximo de tuplas que queremos que nos devuelva el SGBD: s.setMaxRows(maxTuplas); • Se puede limitar el número máximo de segundos que queremos que espere el Driver: s.setQueryTimeout(maxSegundos); A. Goñi. Dpto. LSI, UPV/EHU 113 Interfaz ResultSet • Sirve para trabajar con las respuestas a las preguntas SQL realizadas boolean b = r.next(); // r es un objeto de tipo ResultSet se posiciona en la siguiente tupla del resultado; devuelve true si hay o false si se ha llegado al final • Existen métodos get que devuelven el valor de la tupla actual correspondiente a un atributo de la tabla respuesta. – El atributo se puede identificar por el nombre dado en la parte SELECT de la pregunta o por su posición en la misma – El tipo del atributo puede ser conocido o no A. Goñi. Dpto. LSI, UPV/EHU 114 Interfaz ResultSet String s = r.getString(numColumna); String s = r.getString(nombreColumna); int i = r.getInt(numColumna); int i = r.getInt(nombreColumna); boolean b = r.getBoolean(numColumna); boolean b = r.getBoolean(nombreColumna); // si conocemos el tipo del atributo Object o = r.getObject(numColumna); Object o = r.getObject(nombreColumna); // si no conocemos el tipo del atributo A. Goñi. Dpto. LSI, UPV/EHU 115 ResultSet rs = s.executeQuery(“SELECT * FROM BILLETES”); rs.next() true int i = rs.getInt(1); 1 String est = rs.getString(2); OCUPADO A. Goñi. Dpto. LSI, UPV/EHU 116 ResultSet rs = s.executeQuery(“SELECT * FROM BILLETES”); rs.next() true int i = rs.getInt(“NUMERO”); 0 String est = rs.getString(“ESTADO”); OCUPADO A. Goñi. Dpto. LSI, UPV/EHU 117 ResultSet rs = s.executeQuery(“SELECT * FROM BILLETES”); rs.next() true Object i = rs.getObject(1); i.toString() “2” String est = rs.getString(“ESTADO”); LIBRE A. Goñi. Dpto. LSI, UPV/EHU 118 ResultSet rs = s.executeQuery(“SELECT * FROM BILLETES”); rs.next() false A. Goñi. Dpto. LSI, UPV/EHU 119 Usando sentencias con parámetros • Algunas sentencias SQL se repiten varias veces cambiando sólo algunos valores. – Se pueden parametrizar Sea c un objeto de Connection PreparedStatement s = c.prepareStatement(“SELECT NOMBRE FROM PERSONAS WHERE CIUDAD=? AND EDAD>?”); s.setString(1,“SS”); // Poner SS en 1er parámetro s.setInt(2,25); // Poner 25 en 2º parámetro ResultSet r = s.executeQuery(); // ... // Se puede reejecutar s.setString(1,“Bi”);.. A. Goñi. Dpto. LSI, UPV/EHU 120 A. Goñi. Dpto. LSI, UPV/EHU 121 A. Goñi. Dpto. LSI, UPV/EHU 122 A. Goñi. Dpto. LSI, UPV/EHU 123 A. Goñi. Dpto. LSI, UPV/EHU 124 1 3 4 Seleccionar una BD Access previamente creada A. Goñi. Dpto. LSI, UPV/EHU 2 Agregar y escoger “MAccess Driver” 125 A. Goñi. Dpto. LSI, UPV/EHU 126 A. Goñi. Dpto. LSI, UPV/EHU 127 EL CÓDIGO ANTERIOR PODRÍA SUSTITUIRSE POR EL SIGUIENTE, DONDE SE USA UNA LLAMADA A UNA SENTENCIA SQL PARAMETRIZADA A. Goñi. Dpto. LSI, UPV/EHU 128 A. Goñi. Dpto. LSI, UPV/EHU 129 Conexión con el nivel de presentación • La clase ServidorGestorBilletes es una clase que contiene la LÓGICA DEL NEGOCIO – inicializarSala y getBillete • Y que realiza llamadas al NIVEL DE DATOS – Son todas las sentencias JDBC • Para realizar la conexión con el NIVEL DE PRESENTACIÓN, bastará con implementar la interfaz utilizada por el mismo y asignar un objeto de la lógica del negocio al objeto de presentación A. Goñi. Dpto. LSI, UPV/EHU 130 HAY QUE INDICAR EXPLÍCITAMENTE QUE SE IMPLEMENTA LA INTERFAZ CREAR OBJETO LÓGICA DEL NEGOCIO CREAR OBJETO DE PRESENTACIÓN ASIGNAR ESA LÓGICA DEL NEGOCIO AL OBJETO PRESENTACIÓN A. Goñi. Dpto. LSI, UPV/EHU 131 Interface GestorBilletes Clase PedirBillete usa gestorBilletes: GestorBilletes setGestorBilletes (GestorBilletes) getBillete(String):int clase ServidorGestorBilletesBD getBillete(String):int inicializarSala(int) crearTablaBilletes() A. Goñi. Dpto. LSI, UPV/EHU 132 4. Mecanismos de serialización en Java A. Goñi. Dpto. LSI, UPV/EHU 133 Serialización de objetos • Java ofrece unos servicios de serialización de objetos que CONVIERTEN OBJETOS EN SERIES DE BYTES • Son útiles para: – proporcionar persistencia de objetos, ya que una vez convertidos los objetos en bytes se pueden guardar en Streams de bytes (ficheros) – enviar mensajes entre objetos de distintas máquinas (se serializan dichos mensajes) • Usando sockets, RMI (Remote Method Invocation) A. Goñi. Dpto. LSI, UPV/EHU 134 CLASES Persona dni: int nombre: String suCoche: Coche Coche matricula: String modelo: String susProp: Vector OBJETOS juan:Persona 17832423 “Juan Pérez” coche1 elena:Persona 17114452 “Elena Pérez” coche1 coche1:Coche 3541 CGH “Citroen C4” <juan,elena> Se desea guardar el contenido de los objetos juan y elena en un fichero, o enviarlo por la red… A. Goñi. Dpto. LSI, UPV/EHU Por ejemplo, si se quisiera almacenar los objetos en el fichero personas.txt… 135 PrintWriter fichero = new PrintWriter (new FileWriter("personas.txt")); fichero.println(juan.toString()); fichero.println(elena.toString()); public class Persona { … public String toString () { return dni+ "/"+nombre+ + "/“ + suCoche.getMat() + "/"+ suCoche.getModelo();} } personas.txt 17832423/Juan Pérez/3541 CGH/Citroen C4 17114452/Elena Pérez/3541 CGH/Citroen C4 … pero hay una pega al almacenar los valores. ¿Cómo se sabe “3541 CGH/Citroen C4” es el MISMO objeto? A. Goñi. Dpto. LSI, UPV/EHU 136 Serialización de objetos • Si se quiere que los objetos de una clase se puedan serializar, en la definición de dicha clase hay que indicar que implementa el interfaz java.io.Serializable • Entonces se pueden leer/escribir objetos en ObjectInputStream/ObjectOutputStream con los métodos readObject/writeObject A. Goñi. Dpto. LSI, UPV/EHU 137 Serialización de objetos • Para que un determinado atributo de una clase no se serialice hay que declararlo como transient – Por ejemplo un atributo que sea un Stream • Los métodos writeObject y readObject funcionan bien con los tipos básicos, Strings, Arrays, Vector, etc. Pero también se pueden redefinir para una determinada clase private void writeObject(java.io.ObjectOutputStream stream) throws IOException; private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException; A. Goñi. Dpto. LSI, UPV/EHU 138 Serialización de objetos • Al hacer writeObject de un objeto se serializan todos los objetos contenidos en él, y así recursivamente. • Hay que tener en cuenta que NO SE SERIALIZAN LOS ATRIBUTOS static A. Goñi. Dpto. LSI, UPV/EHU Por ejemplo, si se quisiera almacenar los objetos en el fichero personas.dat… 139 ObjectOutputStream fichero = new ObjectOutputStream (new FileOutputStream("personas.dat")); fichero.writeObject(juan); fichero.writeObject(elena); public class Persona implements Serializable{ … } writeObject escribirá en personas.dat el objeto juan junto con su objeto incrustado coche1, y después escribirá el objeto elena con su objeto incrustado coche1 (¡¡el mismo!!) A. Goñi. Dpto. LSI, UPV/EHU Por ejemplo, si se quisiera enviar los objetos por la red … 140 ObjectOutputStream socketComoStream = new ObjectOutputStream (socket.getOutputStream()); socketComoStream.writeObject(juan); socketComoStream.writeObject(elena); public class Persona implements Serializable{ … } writeObject enviará por el socket el objeto juan junto con su objeto incrustado coche1, y después enviará el objeto elena con su objeto incrustado coche1 (¡¡el mismo!!) A. Goñi. Dpto. LSI, UPV/EHU 141 Ejemplo de serialización de un vector • Dado un vector: Vector billetes; • Se puede escribir en el fichero billetes.dat ObjectOutputStream sal = new ObjectOutputStream(new FileOutputStream(“billetes.dat”)); sal.writeObject(billetes); • Y se puede leer del fichero billetes.dat ObjectInputStream ent = new ObjectInputStream(new FileInputStream(“billetes.dat”)); billetes=(Vector)ent.readObject(); A. Goñi. Dpto. LSI, UPV/EHU 142 Nueva lógica del negocio para el ejemplo de PEDIR BILLETES utilizando MECANISMOS DE SERIALIZACIÓN para la persistencia A. Goñi. Dpto. LSI, UPV/EHU 143 Interface GestorBilletes Clase PedirBillete usa gestorBilletes: GestorBilletes setGestorBilletes (GestorBilletes) getBillete(String):int clase ServidorGestorBilletesSerializado getBillete(String):int inicializarSala(int) escribir() leer() A. Goñi. Dpto. LSI, UPV/EHU Nueva lógica del negocio para144 el ejemplo de PEDIR BILLETES. Se pide el billete a un servidor (que se encuentra en la máquina “host”) por medio de un SOCKET. Se usan mecanismos de SERIALIZACIÓN para el paso de parámetros y recogida de resultados. A. Goñi. Dpto. LSI, UPV/EHU El servidor está a la espera de 145 peticiones de clientes. Recoge los parámetros y devuelve el resultado por medio de SOCKETS. Utiliza la clase ServidorGestorBilletesBD A. Goñi. Dpto. LSI, UPV/EHU 146 Interface GestorBilletes Clase PedirBillete usa gestorBilletes: GestorBilletes getBillete(String):int setGestorBilletes (GestorBilletes) clase GestorBilletesCliente getBillete(String):int En distintos ordenadores socket clase ServidorGestorBilletesBD getBillete(String):int inicializarSala(int) crearTablaBilletes() A. Goñi. Dpto. LSI, UPV/EHU usa clase Servidor 147 5. Introducción a la computación distribuida con Java RMI A. Goñi. Dpto. LSI, UPV/EHU Índice • • • • • Introducción a RMI Interfaz remota Servidor remoto Cliente Arquitectura RMI: stubs, skeletons, rmiregistry, serialización de parámetros/resultados, política de seguridad • Ejemplo • Conexión entre nivel de presentación, lógica del negocio y nivel de datos A. Goñi. Dpto. LSI, UPV/EHU 148 149 Introducción a RMI • RMI (Remote Method Invocation) es un API; un conjunto de clases y métodos que se encuentran en el paquete (java.rmi)y que sirven para – desarrollar aplicaciones distribuidas en Java – que tengan la misma sintaxis y semántica que aplicaciones no distribuidas • Equivalente a RPC (Remote Procedure Call) • Existen otros estándares como CORBA – Más extenso ya que sirve para poder llamar a servicios construidos con distintos lenguajes de programación (y no sólo Java) A. Goñi. Dpto. LSI, UPV/EHU 150 Introducción a RMI Clase Cliente1 .... Clase ClienteN .... EN NODOS CLIENTE Clase GestorBilletes - listaBilletes: Vector - maxBilletes: int + getGestor(): GestorBilletes + getBillete(nom: String): int // Dev. nº billete asignado o -1 si no hay -- Clase con 1 sola instancia (Singleton) -- El método static getGestor() la devuelve EN EL NODO SERVIDOR No es posible realizar lo siguiente en una clase cliente: GestorBilletes g = GestorBilletes.getGestor(); return g.getBillete("Kepa Sola"); Es un objeto de una máquina virtual Java remota A. Goñi. Dpto. LSI, UPV/EHU 151 Introducción a RMI • Para construir una aplicación Cliente/Servidor donde un cliente acceda a un servicio remoto (clase remota) usando RMI hay que: • 1.- Definir una interfaz remota • 2.- Implementar dicha interfaz remota (construir el servidor RMI) • 3.- Implementar el cliente RMI que accede al servicio remoto A. Goñi. Dpto. LSI, UPV/EHU 152 Interfaz remota Interfaz GestorBilletes Cliente1 .... + getBillete(nom: String): int ClienteN .... // Dev. nº billete asignado o -1 si no hay -- INTERFAZ REMOTA RMI permite que se llamen a objetos remotos. Para ello, hay que definir una interfaz REMOTA. Así, en un cliente podría ejecutarse lo siguiente: GestorBilletes g; // Código para obtener la dirección del objeto // remoto y dejarlo en g return g.getBillete(“Kepa Sola”); A. Goñi. Dpto. LSI, UPV/EHU 153 Interfaz Remota // GestorBilletes.java import java.rmi.*; public interface GestorBilletes extends Remote { public int getBillete(String nom) throws RemoteException; } GestorBilletes.java realiza Interfaz java.rmi.Remote -- INTERFAZ REMOTA Interfaz GestorBilletes + getBillete(nom: String): int -- INTERFAZ REMOTA HAY QUE TENER EN CUENTA QUE: • La interfaz remota debe extender java.rmi.Remote • Y todos los métodos definidos en él deben declararse como que pueden lanzar la excepción java.rmi.RemoteException A. Goñi. Dpto. LSI, UPV/EHU 154 Servidor Remoto Clase java.rmi.server.UnicastRemoteObject -- CLASE REMOTA Interfaz java.rmi.Remote -- INTERFAZ REMOTA extiende extiende Clase ServidorGestorBilletes implementa + getBillete(nom: String): int Interfaz GestorBilletes + getBillete(nom: String): int -- CLASE REMOTA -- INTERFAZ REMOTA La clase servidor remoto: • implementa los métodos de la interfaz remota • extiende java.rmi.server.UnicastRemoteObject La clase servidor remoto creará un objeto de sí misma y lo registrará con un nombre (para que objetos de clases clientes accedan a él y ejecuten sus métodos remotamente). A. Goñi. Dpto. LSI, UPV/EHU Servidor Remoto 155 // ServidorGestorBilletes.java import java.rmi.*; import java.rmi.server.UnicastRemoteObject; import java.util.*; public class ServidorGestorBilletes extends UnicastRemoteObject implements GestorBilletes { private Vector listaBilletes = new Vector(); private static int maxBills = 50; public ServidorGestorBilletes() throws RemoteException{} public int getBillete(String nom) throws RemoteException { int num = listaBilletes.size()+1; if (num>maxBills) return -1; listaBilletes.addElement(nom); System.out.println("Asignado billete a: "+nom); return num; } public static void main(String[] args) {...} } A. Goñi. Dpto. LSI, UPV/EHU 156 Servidor Remoto // Método main de ServidorGestorBilletes.java public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try { ServidorGestorBilletes objetoServidor = new ServidorGestorBilletes(); String servicio = "//localhost/gestorBilletes"; // "//localhost:NumPuerto/NombreServicio" // Registrar el servicio remoto Naming.rebind(servicio,objetoServidor); } catch (Exception e) {System.out.println("Error al lanzar el servidor");} } A. Goñi. Dpto. LSI, UPV/EHU 157 Cliente Clase java.rmi.server.UnicastRemoteObject -- CLASE REMOTA extiende Interfaz java.rmi.Remote -- INTERFAZ REMOTA extiende Clase ServidorGestorBilletes implementa + getBillete(nom: String): int + getBillete(nom: String): int -- CLASE REMOTA -- INTERFAZ REMOTA La clase cliente: • busca el objeto remoto que le proporciona el servicio que le interesa. Para ello debe conocer el nombre del servicio, pero no la clase remota (no hace new) • pide a dicho objeto que ejecute un método remoto (definido en la interfaz remota) A. Goñi. Dpto. LSI, UPV/EHU Interfaz GestorBilletes usa Clase Cliente ges : GestorBilletes -- CLASE CLIENTE 158 Cliente Cliente.java // import java.rmi.*; public class Cliente { public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); GestorBilletes objRemoto; String nomServ = "rmi://localhost/gestorBilletes"; // "rmi://DireccionIP:NumPuerto/NombreServicio" try { objRemoto = (GestorBilletes)Naming.lookup(nomServ); int b = objRemoto.getBillete(args[0]); if (b==-1) System.out.println("No hay billetes"); else System.out.println("Obtenido : "+b); } catch (Exception e) { System.out.println("Error... ");}}} A. Goñi. Dpto. LSI, UPV/EHU 159 Los clientes RMI siguen un patrón de diseño INTERFACE Clase Cliente Interfaz InterfazRemota usa Permite que la clase cliente ejecute métodos de instancias de otras clases, independientemente de las mismas (sin siquiera saber cómo se llaman). Incluso pueden ser clases que todavía no estén implementadas en el momento de escribir el código de la clase Cliente. A. Goñi. Dpto. LSI, UPV/EHU 160 Arquitectura RMI objRemoto.getBillete("Koldo") RETURN: objRemoto @ObjLocalX 5 Máquina virtual java CLIENTE Máquina virtual java SERVIDOR objServidor.getBillete("Koldo") objServidor @ObjLocalY 1) Hay que conectar el objeto remoto (objRemoto) con el objeto servidor (objServidor) para que las llamadas a métodos del primero sean ejecutadas por el segundo 2) Hay que pasar los valores de los parámetros de los métodos del cliente al servidor 3) Hay que pasar los resultados de los métodos del servidor al cliente A. Goñi. Dpto. LSI, UPV/EHU 161 Arquitectura RMI Programa Cliente call return Programa Servidor call return Skeleton Stub Nivel de Referencia Remota Nivel de Referencia Remota Nivel de Transporte Nivel de Transporte Conexión TCP/IP Los objetos Stub y Skeleton se encargan de realizar la conexión y del paso de parámetros y resultados. A. Goñi. Dpto. LSI, UPV/EHU 162 Arquitectura RMI GestorBilletes objRemoto = (GestorBilletes)Naming.lookup("rmi://IPServidor/gestorBilletes"); lookup BUSCA y objRemoto.getBillete("Koldo"); OBTIENE el objeto STUB @ObjStubX Objeto STUB de la clase ServidorGestorBilletes_Stub Máquina virtual java CLIENTE getBillete "Koldo" RETURN: 5 Máquina virtual java SERVIDOR Objeto SKELETON de la clase ServidorGestorBilletes_Skeleton @ObjSkelY objServidor @ObjServY objServidor.getBillete("Koldo") A. Goñi. Dpto. LSI, UPV/EHU Objeto de la clase ServidorGestorBilletes 163 rmic: Generación de stubs y skeletons rmic ServidorGestorClientes SE OBTIENEN LAS CLASES: ServidorGestorClientes_Stub.class ServidorGestorClientes_Skeleton.class El fichero ServidorGestorClientes_Stub.class debe quedar accesible a la máquina cliente. Una posibilidad es copiarlo en ella (ver otra posibilidad más adelante...) rmic es una herramienta que genera de manera automática las clases STUB y SKELETON a partir de la clase remota (ServidorGestorClientes.class) A. Goñi. Dpto. LSI, UPV/EHU 164 Arquitectura RMI GestorBilletes objRemoto = (GestorBilletes)Naming.lookup("rmi://IPServidor/gestorBilletes"); ¿Cómo se sabe cuál es la clase STUB de la cual hay que crear un objeto? GestorBilletes es un interface La clase en este caso es ServidorGestorBilletes_Stub.class Máquina virtual java situada en IPServidor puerto 1099 RMIREGISTRY gestorBilletes @ObjServidor ServidorGestorBilletes_Stub .... .... Naming.rebind("gestorBilletes",objetoServidor); A. Goñi. Dpto. LSI, UPV/EHU 165 rmiregistry • Es un servidor de nombres que relaciona objetos con nombres • Hay que lanzarlo como proceso independiente en la máquina servidor – En la misma máquina que el servidor RMI • En Unix – rmiregistry & (en Unix) – rmiregistry numPuerto & • En Windows – rmiregistry [num. puerto] – start rmiregistry [num. puerto] A. Goñi. Dpto. LSI, UPV/EHU 166 rmiregistry • También se puede lanzar desde una aplicación Java (en el servidor RMI) java.rmi.registry.LocateRegistry.createRegistry(p) Crea el proceso rmiregistry en el puerto p. El rmiregistry lanzado no acaba aunque acabe el servidor RMI Lanza una excepción si el puerto está ocupado try { java.rmi.registry.LocateRegistry.createRegistry(1099); } catch (Exception e) {System.out.println(“Rmiregistry ya lanzado“+e.toString());} Código que lanza el rmiregistry y controla la excepción que se puede levantar al reejecutar varias veces el servidor RMI A. Goñi. Dpto. LSI, UPV/EHU 167 Serialización de parámetros/resultados • En principio, los stubs y los servidores RMI se pasan los objetos enviados como parámetros y los resultados REALIZANDO UNA COPIA DE SUS VALORES (y de los objetos incluidos en ellos, recursivamente). – No se pasan referencias a un objeto remoto. • Para ello, se usan los mecanismos de serialización de Java las clases de dichos objetos deben implementar la interfaz Serializable A. Goñi. Dpto. LSI, UPV/EHU 168 RMIREGISTRY y ServidorGestorBilletesBD en la misma máquina (IPServidor), pero cada uno en DISTINTAS MÁQUINAS VIRTUALES JAVA mainServidorGestorBilletesBD objRemoto:ServidorGestorBillet esBD new RmiRegistry está escuchando en un puerto (1099) Mirar métodos de LocateRegistry.createRegistry exportObject(PuertoAnonimo) Naming stub: ServidorGestorBilletesBD _Stub Rmiregistry rebind("localhost:1099/gestorBilletes",objRemoto) getRef() new(referenciaAObjRemoto) referenciaAObjRemoto : PedirBillete Naming SOCKET("localhost",1099,"gestorBilletes",stub) lookup("rmi://IpServidor:1099/gestorBilletes") SOCKET("IPservidor",1099,"gestorBilletes") SOCKET(stub) Se pasa el objeto STUB serializado. El cliente cargará dinámicamente la clase ServidorGestorBilletesBD_Stub stub stub: ServidorGestorBilletesBD _Stub getBillete("Pepe") SOCKET("IPServidor",PuertoAnonimo,"gestorBilletes","getBillete","Pepe") numBilleteParaPepe PresentacionRemoto en otra máquina A. Goñi. Dpto. LSI, UPV/EHU SOCKET(numBilleteParaPepe) 169 java.security.policy • Un programa Java debe especificar un gestor de seguridad que determine su política de seguridad. • Algunas operaciones requieren que exista dicho gestor. En concreto, las de RMI. – RMI sólo cargará una clase serializable desde otra máquina si hay un gestor de seguridad que lo permita – Se puede establecer un gestor de seguridad por defecto para RMI de la siguiente manera: System.setSecurityManager( new RMISecurityManager()); A. Goñi. Dpto. LSI, UPV/EHU 170 java.security.policy • El gestor de seguridad por defecto de RMI utiliza una política muy restrictiva. – Sólo se pueden ejecutar STUBs del CLASSPATH local • Se puede cambiar, indicando a la máquina virtual Java otro fichero de política de seguridad: java -Djava.security.policy=java.policy Clase Nombre de fichero (por ejemplo) Contenido del fichero java.policy: grant { permission java.security.AllPermission; }; NOTA: ESTO NO ES NECESARIO PARA VERSIONES ANTERIORES AL JDK1.2 A. Goñi. Dpto. LSI, UPV/EHU 171 Para no tener que copiar el STUB en el cliente... • Al lanzar el CLIENTE hay que dar una URL para que el Naming.lookup acceda al stub allí java -Djava.rmi.server.codebase=http://... -Djava.security.policy=... cliente NOTA: la URL debe acabar con / http://www.ehu.es INCORRECTO http://www.ehu.es/ CORRECTO A. Goñi. Dpto. LSI, UPV/EHU Servidor Remoto accede a BD 172 // ServidorGestorBilletes.java import java.rmi.*; import java.sql.*; import java.rmi.server.UnicastRemoteObject; import java.util.*; public class ServidorGestorBilletesBD extends UnicastRemoteObject implements GestorBilletes {private static Connection conexion; private static Statement sentencia; public ServidorGestorBilletesBD() throws RemoteException{ try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:Bill"); sentencia=conexion.createStatement(); conexion.setAutoCommit(false);// Habrá que hacer COMMITs } catch(Exception e) { System.out.println(”Error: "+e.toString());} } A. Goñi. Dpto. LSI, UPV/EHU Servidor Remoto accede a BD 173 public int getBillete(String nom) throws RemoteException { // Devuelve nº billete, -1 si no hay, -2 si hay problemas String pregSQL = "SELECT NUM FROM BILLETES”+ " WHERE ESTADO='LIBRE'"; try{ ResultSet rs = sentencia.executeQuery(pregSQL); if (rs.next()) { String num = rs.getString("NUM"); int act = sentencia.executeUpdate("UPDATE BILLETES"+ " SET ESTADO='OCUPADO', NOMBRE = '"+nom+ "' WHERE NUM="+num+" AND ESTADO='LIBRE'"); conexion.commit(); int n= Integer.parseInt(num); if (act>0) return n; // Núm. billete asignado return -2; } // Otro ya ha OCUPADO ese billete else return -1; } // No había ninguno libre catch (SQLException e) {System.out.println("Error: "+e.toString());} return -2; // Que prueben otra vez a llamar A. Goñi. Dpto. LSI, UPV/EHU } Servidor Remoto accede a BD // Método main de ServidorGestorBilletesBD.java public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try { ServidorGestorBilletesBD objetoServidor = new ServidorGestorBilletesBD(); String maquina = "//localhost/"; String servicio = "gestorBilletes"; String servicioRemoto = maquina+servicio; // Registrar el servicio remoto Naming.rebind(servicioRemoto,objetoServidor); }catch (Exception e) {System.out.println("Error: "+e.toString());} } } A. Goñi. Dpto. LSI, UPV/EHU 174 175 Conexión entre nivel de presentación, lógica del negocio y datos • Hasta ahora hemos considerado que en el nivel de presentación, el objeto con la lógica del negocio se encuentra en un atributo (de tipo interface Java) • Utilizando RMI se puede seguir con esa misma idea, pero en este caso la interfaz es remota • En vez de asignarle al objeto de presentación, el objeto con la lógica del negocio se puede hacer que sea el objeto de presentación quien lo busque (usando lookup). • Cambiar la lógica del negocio consiste en sustituir un objeto por otro en el servidor. A. Goñi. Dpto. LSI, UPV/EHU ARQUITECTURA FÍSICA EN 2 NIVELES: CLIENTE GORDO / SERVIDOR FLACO Clase Presentacion Interface LogicaNegocio hacerX(… ) hacerY(… ) usa logNe: LogicaNegocio setLogicaNegocio (l: LogicaNegocio) // Permite cambiar lógica // negocio en tº ejecuc. -- En esta clase se llama -- a la lógica del negocio: Por ej.: logNe.hacerX(…) clase LogicaNegocioConcreta hacerX(…) // Implementaciones hacerY(…) // llaman al nivel de // datos (usan JDBC) CREAR LA INTERFAZ GRÁFICA Y ASIGNAR LÓGICA DEL NEGOCIO: Presentacion p = new Presentacion(); p.setLogicaNegocio(new LogicaNegocioConcreta()); p.setVisible(true); A. Goñi. Dpto. LSI, UPV/EHU 176 ARQUITECTURA FÍSICA Interface LogicaNegocio EN 3 NIVELES hacerX(… ) usando RMI hacerY(… ) Clase Presentacion usa interfaz Remota logNe: LogicaNegocio setLogicaNegocio (String nombreServicio) // Por ejemplo aquí se // puede asignar a logNe -- En esta clase se llama -- a la lógica del negocio: Por ej.: logNe.hacerX(…) -- Para asignar la lógica -- del negocio se usa Naming.lookup y NomSer A. Goñi. Dpto. LSI, UPV/EHU clase LogicaNegocioConcreta hacerX(…) // Implementaciones hacerY(…) // llaman al nivel de // datos (usan JDBC) -- La lógica del negocio se crea -- y se exporta usando: Naming.rebind y dando un nombre de servicio: NomSer 177 178 6. Tecnología Java para construcción de aplicaciones Web Una introducción a Applets y Java Server Pages (JSP) A. Goñi. Dpto. LSI, UPV/EHU 179 Índice • Introducción • Applets – Introducción y algunas características técnicas – Nivel de presentación con Applets – Nivel de lógica del negocio con Applets • Servlets • Java Server Pages (JSPs) – Introducción y algunas características técnicas – Nivel de presentación con JSPs – Nivel de lógica del negocio con JSPs A. Goñi. Dpto. LSI, UPV/EHU 180 Introducción • Aplicación Web: es una aplicación que se ejecuta en un navegador Web – Se pueden separar los 3 niveles: presentación, lógica del negocio y datos – El nivel de presentación se ejecuta en el cliente pero se “despliega” en el servidor – Que haya que cambiar el nivel de presentación no implica reinstalar todos los clientes – Se puede añadir parte de la lógica del negocio a la presentación por motivos de eficiencia: • Se reducirían llamadas a la lógica del negocio • Si hubiera que cambiar la lógica del negocio no implicaría reinstalar todos los clientes A. Goñi. Dpto. LSI, UPV/EHU 181 Introducción • La tecnología Java incluye la posibilidad de crear aplicaciones Web utilizando Applets y Java Server Pages (JSPs) • Son dos filosofías distintas: – Usando applets se descargan aplicaciones desde el servidor a los clientes. • En los clientes se ejecutan dichas aplicaciones. – Con JSPs, en el servidor se ejecutan aplicaciones que generan código HTML de manera dinámica, el cual se envía a los clientes • En los clientes se visualiza el resultado HTML. A. Goñi. Dpto. LSI, UPV/EHU 182 Applets Programas Java que se ejecutan en un navegador Web. Son una herramienta poderosa que soporta la programación en el lado del cliente, muy importante en la Web. Cliente Navegador Applet A. Goñi. Dpto. LSI, UPV/EHU 183 Applets: Restricciones y Ventajas • (-) Un applet no puede acceder al disco local • Restringir lectura para que no pueda transmitirse información local por internet • Restringir la escritura para que no se puedan escribir virus en el disco local • (-) No se puede hacer una conexión de red a otro nodo de Internet que no sea aquél de donde se ha descargado • Existe la posibilidad de firmar digitalmente applets para eliminar algunas restricciones • (-) Ejecución más lenta ya que hay que descargarlos primero • Mejor comprimir todos los applets en ficheros JAR • (+) No hay que instalarlos en el cliente. Los applets son independientes de la plataforma. • (+) No hay que preocuparse por problemas de seguridad A. Goñi. Dpto. LSI, UPV/EHU 184 Métodos de los applets • Para escribir un applet, hay que heredar de la clase Applet/JApplet e implementar unos métodos • init( ): – Se ejecuta automáticamente cuando se inicializa el applet. Hay que proporcionar una implementación obligatoriamente • start( ): – Se ejecuta cada vez que el applet se hace visible, como resultado de alguna acción en el navegador.Se llama siempre después de init() • stop( ): – Se ejecuta cada vez que el applet deja de verse • destroy( ): – Se ejecuta cuando el applet se descarga definitivamente (implementarlo si hubiera que liberar recursos) • No hay por qué implementar el método main() A. Goñi. Dpto. LSI, UPV/EHU 185 Ejecución de applets • Para ejecutar un applet: – Se incrusta en una página Web usando una etiqueta (tag) – Se visualiza en un navegador que contenga la máquina virtual java apropiada. • Las etiquetas apropiadas son: – Para applets de AWT (Applet), se usa el tag “applet” • <applet code=“applet” width=100 height=50> </applet> – Para applets Swing (JApplet), depende del navegador • Con Internet Explorer se necesita cargar un mecanismo de extensión que es un control ActiveX • Con Netscape hay que cargar el plug-in apropiado • Como el programador no sabe con qué navegador se cargará la página, hay que proporcionar las etiquetas (tags) para ambos • La herramienta Appletviewer visualiza applets (de AWT y Swing) incrustados con el tag Applet A. Goñi. Dpto. LSI, UPV/EHU 186 Ejecución de applets <html><head><title>Applet1</title></head><hr> <OBJECT classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93“ width="100" height="50" align="baseline" codebase="http://java.sun.com/products/plugin/1.2.2/jinstall1_2_2-win.cab#Version=1,2,2,0"> <PARAM NAME="code" VALUE="Applet1.class"> <PARAM NAME="codebase" VALUE="."> <PARAM NAME="type" VALUE="application/x-java-applet;version=1.2.2"> <COMMENT> <EMBED type="application/x-java-applet;version=1.2.2" width="200" height="200" align="baseline“ code="Applet1.class" codebase="." pluginspage="http://java.sun.com/products/plugin/1.2/plugin-install.html"> <NOEMBED> </COMMENT> No hay soporte para los applets </NOEMBED> </EMBED> </OBJECT> <hr></body></html> A. Goñi. Dpto. LSI, UPV/EHU Nivel de presentación usando Applets • En un Applet/JApplet se pueden incluir todos los elementos gráficos de los paquetes AWT y SWING de Java • En un Applet se puede dibujar, cargar imágenes y escuchar sonidos A. Goñi. Dpto. LSI, UPV/EHU 187 188 Nivel lógica del negocio con Applets • Para obtener la lógica del negocio desde un applet, se puede usar RMI: la conexión sólo puede hacerse con el servidor Web public void init() // Método inicialización del Applet {try {… String maquina = this.getCodeBase().getHost(); gestorBilletes = (GestorBilletes)Naming.lookup("rmi://"+maquina+":“ +numPuerto+servicio); } catch(Exception e) { // Error al cargar la lógica del negocio } LA LÓGICA DEL NEGOCIO SE IMPLEMENTARÍA IGUAL A. Goñi. Dpto. LSI, UPV/EHU 189 Nivel lógica del negocio con Applets • Para llamar a la lógica del negocio, se usa el objeto de gestorBilletes void jButton1_actionPerformed(ActionEvent e) {try { String nombre = jTextField1.getText(); int res = gestorBilletes.getBillete(nombre); if (res>0) jTextArea1.append("Asignado. \nReferencia: "+res+"\n"); else if (res==-1) jTextArea1.append("No hay billetes libres\n"); else if (res==-2) jTextArea1.append("Error: Inténtelo de nuevo.\n"); } catch (Exception ex) {jTextArea1.append("Error: "+ex.toString()+"\n");} } A. Goñi. Dpto. LSI, UPV/EHU 190 Se puede validar la entrada en el nivel de presentación • Más eficiente: sólo se llama a la lógica del negocio con entradas correctas void jButton1_actionPerformed(ActionEvent e) {try { String nombre = jTextField1.getText(); if ((nombre.length()<5) || (existeNoLetra(nombre))) jTextArea1.append("Error: por lo menos 5 letras\n"); else { int res = gestorBilletes.getBillete(nombre); if (res>0) ...}} // CÓDIGO ANTERIOR QUE LLAMA catch (Exception ex) {...} // A LA LÓGICA DEL NEGOCIO static boolean existeNoLetra(String s) { for (int i=0;i<s.length();i++) { char c = s.charAt(i); if (!((c >= 'a') && (c <= 'z') || (c >= 'A') && (c <= 'Z'))) // Añadir á,é,..ñ,Ñ !! return true;} return false;} A. Goñi. Dpto. LSI, UPV/EHU 191 Ejemplo del applet PedirBilleteAp Usando Appletviewer A. Goñi. Dpto. LSI, UPV/EHU Usando un navegador ¿Validar la entrada es nivel de presentación o lógica del negocio? 192 • Validar la entrada tiene que ver con la presentación, con la interacción con el usuario, que ha escrito mal los datos: debe hacerse en el nivel de presentación • Sin embargo, a veces no está tan clara la diferencia entre nivel de presentación y lógica del negocio • En una aplicación Web no importa tanto, ya que el nivel de presentación “se instala” en el lado del servidor y se ejecuta en los clientes. Cambiar la lógica del negocio o presentación NO requiere reinstalar los clientes • Regla a observar: intuir cambios futuros y hacer el software extensible ante esos cambios A. Goñi. Dpto. LSI, UPV/EHU 193 Servlets Aplicaciones Java, que devuelven como resultado HTML. Normalmente se utilizan para la generación de páginas dinámicas o control Servidor Html Servlet A. Goñi. Dpto. LSI, UPV/EHU 194 Plantilla de un Servlet Simple import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ServletTemplate extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // En "request" se leen las cabeceras HTTP y los // datos que se hayan escrito en formularios // Se usa "response" para especificar el código de // estado de la respuestas y las cabeceras HTTP (ej. // tipo del contenido, cookies, etc.) PrintWriter out = response.getWriter(); // Se usa "out" para enviar el contenido al navegador } } A. Goñi. Dpto. LSI, UPV/EHU 195 Ejemplo de Servlet public class HolaWWW extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " + "Transitional//EN\">\n"; out.println(docType + "<HTML>\n" + "<HEAD><TITLE>Hola WWW</TITLE></HEAD>\n" + "<BODY>\n" + "<H1>Hola WWW</H1>\n" + "</BODY></HTML>");}} A. Goñi. Dpto. LSI, UPV/EHU 196 Java Server Pages (JSP) Páginas HTML con código Java embebido. En la primera invocación a un JSP el sistema lo transforma en un Servlet Servidor JSP Html <HTML> %%Java </HTML> A. Goñi. Dpto. LSI, UPV/EHU 197 Scriptlet JSP • <% código java %> – Código Java insertado en una página JSP. Se procesa en el momento de solicitarla – Se pueden usar variables predefinidas: request, response, out, session, application, config, y pageContext <html><body> <H1> Ejemplo de código Java incrustado </H1><P> <% for (int i=0;i<3;i++) out.println("<H2> Hola número "+i+"</H2><BR>"); %> <P> <H1> Adiós </H1> </body></html> A. Goñi. Dpto. LSI, UPV/EHU out: objeto con la salida HTML 198 Scriptlet JSP <html><body> <H1> Ejemplo de código Java incrustado </H1><P> <% for (int i=0;i<3;i++) out.println("<H2> Hola número "+i+"</H2><BR>"); %> <P> <H1> Adiós </H1> </body></html> A. Goñi. Dpto. LSI, UPV/EHU 199 JSP Expression • <%= expression %> – Expresión que se evalúa y se coloca en la salida – Se usan variables predefinidas: request, response, out, session, application, config, y pageContext <html><body> <H1> Ejemplo de código Java incrustado </H1><P> <% for (int i=0;i<3;i++){ %><H2> Hola número <%=i%></H2><BR><%} %> <P> <H1> Adiós </H1> </body></html> A. Goñi. Dpto. LSI, UPV/EHU 200 Expresión JSP <html><body> <H1> Ejemplo de código Java incrustado </H1><P> <% for (int i=0;i<3;i++){ %><H2> Hola número <%=i%></H2><BR><%} %> <P> <H1> Adiós </H1> </body></html> A. Goñi. Dpto. LSI, UPV/EHU 201 Declaración JSP • <%! código %> – El código se ejecuta sólo la primera vez que se carga la página. – Puede servir para definir e inicializar variables que queremos “perduren” <html><body> <H1> Ejemplo de código Java incrustado </H1><P> <%! int i=0; %> <% for (int j=i;j<i+3;j++){ %><H2> Hola número <%=j%></H2><BR><%} i=i+3; %> <P> <H1> Adiós </H1> </body></html> A. Goñi. Dpto. LSI, UPV/EHU 202 Declaración JSP <html><body> <H1> Ejemplo de código Java incrustado </H1><P> <%! int i=0; %> <% for (int j=i;j<i+3;j++){ %><H2> Hola número <%=j%></H2><BR><%} i=i+3; %> <P> <H1> Adiós </H1> </body></html> Primera ejecución del JSP Segunda ejecución del JSP A. Goñi. Dpto. LSI, UPV/EHU 203 Directiva JSP page • <%@ page atributo="valor" %> • Órdenes al motor de servlets sobre configuración – import =“nombre de clase" • Si se usan clases que hay que importar – contentType="MIME-Type" • Por defecto text/html • Podría ser text/vnd.wap.wml (para WAP) – errorPage=“URL" • Se establece una URL a mostrar si ocurre error en el JSP – isErrorPage="true|false" • Si es una página que muestra un error (aparecerá como URL en la directiva errorPage de alguna página JSP) • Puede usarse la variable “exception” (objeto con la excep.) A. Goñi. Dpto. LSI, UPV/EHU 204 Comentarios JSP • <%-- comentario JSP --%> • El comentario es ignorado por el traductor de la página JSP al servlet – Es un comentario sólo visible en la página JSP • Si se desea tener un comentario que sea visible en la página HTML entonces hay que usar <!– comentario HTML --> – Visible sólo en el código fuente HTML, no en el navegador que visualiza dicho código – NOTA: Los scriptlets JSP, directivas JSP y acciones JSP dentro de un comentario HTML SÍ SE EJECUTAN • Se podría enviar un comentario generado de manera dinámica A. Goñi. Dpto. LSI, UPV/EHU 205 Nivel de presentación con JSPs • El nivel de presentación sirve para interactuar con el usuario • Obtener entrada del usuario – Utilización de formularios • Mostrar una salida, resultados,… al usuario – Se puede utilizar toda la potencia del HTML y mostrar: textos, gráficos, sonidos, vídeo, etc. A. Goñi. Dpto. LSI, UPV/EHU 206 Entrada de datos: Formularios HTML A. Goñi. Dpto. LSI, UPV/EHU 207 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD><TITLE> Formulario </TITLE></HEAD> <BODY BGCOLOR="#FDF5E6"> <H2 ALIGN="CENTER">Pantalla de introducción de datos </H2> <FORM ACTION="http://localhost:8080/alfredo/verDatos.jsp"> <CENTER> Nombre: <INPUT TYPE="TEXT" NAME="nombre" VALUE="Filemón"><BR> Apellido: <INPUT TYPE="TEXT" NAME="apellido" VALUE="Pi"><P> <INPUT TYPE="CHECKBOX" NAME="publicidad" CHECKED>Estoy interesado en recibir publicidad<BR> <INPUT TYPE="CHECKBOX" NAME="futbol">Me gusta el fútbol<P> Sexo: <BR> <INPUT TYPE="RADIO" NAME="sexo" VALUE="hombre"> Hombre <BR> <INPUT TYPE="RADIO" NAME="sexo" VALUE="mujer"> Mujer <P> A. Goñi. Dpto. LSI, UPV/EHU 208 Mi afición preferida: <SELECT NAME="aficion"> <OPTION VALUE="monte"> Ir al monte <OPTION VALUE="bailar"> Bailar <OPTION VALUE="estudiar" SELECTED>Estudiar </SELECT> <P> Cuéntame tu vida: <TEXTAREA NAME="vida" ROWS=15 COLS=60 WRAP="SOFT"></TEXTAREA> <INPUT TYPE="SUBMIT" value="Enviar datos"> <!-Pulsar aquí para enviar los datos --> </CENTER> </FORM> A. Goñi. Dpto. LSI, UPV/EHU 209 Entrada de datos: formularios A. Goñi. Dpto. LSI, UPV/EHU Recoger datos de formularios <html><body> <H1> Estos son los datos recogidos: </H1> <% String nombre = request.getParameter("nombre"); String apellido = request.getParameter("apellido"); out.println("Nombre es: "+nombre+" "+apellido); String sexo = request.getParameter("sexo"); String futbol = request.getParameter("futbol"); String publicidad = request.getParameter("publicidad"); String vida = request.getParameter("vida"); String aficion = request.getParameter("aficion"); out.println("<BR>Sexo: "+sexo); out.println("<BR>Publicidad: "+publicidad); out.println(" Fútbol: "+futbol); out.println(" Afición preferida: "+aficion); out.println("<BR>Vida:<BR>"+vida); %> </body></html> request. getParameter ("PARAM") A. Goñi. Dpto. LSI, UPV/EHU 210 <%-- Comentario JSP: no aparece en el HTML, pero sí en el SERVLET --%> 211 <%-- Usamos JSP directive para importar paquetes Java: --%> <%@ page import="java.util.*" %> <%-- Declaraciones JSP: se ejecutan sólo cuando se carga el servlet: --%> <%! long tiempoDeCarga= System.currentTimeMillis(); Date fechaDeCarga = new Date(); int numAccesos = 0; %> <html><body> <img SRC="imagenReloj.jpg" height=218 width=291></img> <%-- A continuación se usan expresiones JSP --%> <H1>Fecha de la primera carga de la página: <%= fechaDeCarga %> </H1> <H1>Fecha actual: <%= new Date() %></H1> <H2>Página activa <%= (System.currentTimeMillis()-tiempoDeCarga)/1000 %> segs</H2> <H3>Página accedida <%= ++numAccesos %> veces desde <%= fechaDeCarga %></H3> <%-- Un "scriptlet" que escribe en la salida estándar del servidor y en la página visualizada por el cliente. Es código Java, y se usa la variable predefinida "out"--%> <% System.out.println("Adiós (no se verá en el navegador)"); out.println("Agur (se verá en el navegador)"); %> <P> <!-- Comentario HTML: aparece como comentario en el HTML, pero no se verá en el navegador del cliente. A continuación hay código HTML para que se cargue un sonido en la página y que funcione en Netscape y en Internet Explorer --%> <EMBED SRC="bong.wav" HIDDEN="true" AUTOSTART="true"> <NOEMBED> <BGSOUND SRC="bong.wav"> </NOEMBED> <P> <a href="bong.wav"> Haz click en este link y oirás el sonido también </a> A. Goñi. Dpto. LSI, UPV/EHU </body></html> MOSTRAR LA SALIDA CON HTML <%-- Comentario JSP: no aparece en el HTML, pero sí en el SERVLET --%> 212 <%-- Usamos JSP directive para importar paquetes Java: --%> <%@ page import="java.util.*" %> <%-- Declaraciones JSP: se ejecutan sólo cuando se carga el servlet: --%> <%! long tiempoDeCarga= System.currentTimeMillis(); Date fechaDeCarga = new Date(); int numAccesos = 0; %> <html><body> <img SRC="imagenReloj.jpg" height=218 width=291></img> <%-- A continuación se usan expresiones JSP --%> <H1>Fecha de la primera carga de la página: <%= fechaDeCarga %> </H1> <H1>Fecha actual: <%= new Date() %></H1> <H2>Página activa <%= (System.currentTimeMillis()-tiempoDeCarga)/1000 %> segs</H2> <H3>Página accedida <%= ++numAccesos %> veces desde <%= fechaDeCarga %></H3> <%-- Un "scriptlet" que escribe en la salida estándar del servidor y en la página visualizada por el cliente. Es código Java, y se usa la variable predefinida "out"--%> <% System.out.println("Adiós (no se verá en el navegador)"); out.println("Agur (se verá en el navegador)"); %> <P> <!-- A continuación hay código HTML para que se cargue un sonido en la página y que funcione en Netscape y en Internet Explorer. Este comentario se verá en el navegador del cliente --%> <EMBED SRC="bong.wav" HIDDEN="true" AUTOSTART="true"> <NOEMBED> <BGSOUND SRC="bong.wav"> </NOEMBED> <P> <a href="bong.wav"> Haz click en este link y oirás el sonido también </a> A. Goñi. Dpto. LSI, UPV/EHU </body></html> <%-- Comentario JSP: no aparece en el HTML, pero sí en el SERVLET --%> 213 <%-- Usamos JSP directive para importar paquetes Java: --%> <%@ page import="java.util.*" %> <%-- Declaraciones JSP: se ejecutan sólo cuando se carga el servlet: --%> <%! long tiempoDeCarga= System.currentTimeMillis(); Date fechaDeCarga = new Date(); int numAccesos = 0; %> <html><body> <img SRC="imagenReloj.jpg" height=218 width=291></img> <%-- A continuación se usan expresiones JSP --%> <H1>Fecha de la primera carga de la página: <%= fechaDeCarga %> </H1> <H1>Fecha actual: <%= new Date() %></H1> <H2>Página activa <%= (System.currentTimeMillis()-tiempoDeCarga)/1000 %> segs</H2> <H3>Página accedida <%= ++numAccesos %> veces desde <%= fechaDeCarga %></H3> <%-- Un "scriptlet" que escribe en la salida estándar del servidor y en la página visualizada por el cliente. Es código Java, y se usa la variable predefinida "out"--%> <% System.out.println("Adiós (no se verá en el navegador)"); out.println("Agur (se verá en el navegador)"); %> <P> <!-- A continuación hay código HTML para que se cargue un sonido en la página y que funcione en Netscape y en Internet Explorer. Este comentario se verá en el navegador del cliente --%> <EMBED SRC="bong.wav" HIDDEN="true" AUTOSTART="true"> <NOEMBED> <BGSOUND SRC="bong.wav"> </NOEMBED> <P> <a href="bong.wav"> Haz click en este link y oirás el sonido también </a> A. Goñi. Dpto. LSI, UPV/EHU </body></html> También se puede validar la entrada 214 en el nivel de presentación con JSPs y JavaScript • Más eficiente: sólo se llama a la lógica del negocio con entradas correctas <HTML><HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> <TITLE>PEDIR BILLETE</TITLE></HEAD> <BODY> <H1> PEDIR BILLETE </H1> <FORM NAME="LE" METHOD="post" ACTION="PedirBillete.jsp"> Nombre: <INPUT TYPE="text" NAME="nombre" onChange=validar();><P> <INPUT TYPE="submit" value="Pedir Billete" > <INPUT type="reset" VALUE="Borrar Datos"> </FORM> <SCRIPT LANGUAGE="JavaScript"> function validar() {var nombre = document.LE.nombre.value; if (nombre.length<5) alert("Error, menos de 5 letras:" + document.LE.nombre.value); document.LE.nombre.value = ""; } </SCRIPT></BODY></HTML> A. Goñi. Dpto. LSI, UPV/EHU 215 Validar la entrada en la presentación JavaScript es un lenguaje de programación interpretado que puede ser ejecutado en un navegador è PERMITE EJECUCIÓN DE CÓDIGO EN EL CLIENTE A. Goñi. Dpto. LSI, UPV/EHU 216 Nivel Lógica del Negocio con JSPs • El nivel de presentación debe limitarse a obtener la entrada del usuario y a devolver resultados – Cómo se obtienen los resultados a partir de la entrada lo deciden/calculan las operaciones propias de la lógica del negocio. • Es mejor que el nivel de presentación lo diseñen “expertos” (diseñadores gráficos) – Ofrecerles la posibilidad de llamar a la lógica del negocio, a ser posible sin que conozcan detalles del lenguaje de programación • Es interesante una arquitectura en varios niveles • Utilizando la tecnología de JSPs, se puede llamar a JavaBeans, que implementen la lógica del negocio A. Goñi. Dpto. LSI, UPV/EHU 217 Java Beans • Un Java Bean es una clase Java que: – Proporciona un constructor sin parámetros • Explícitamente u omitiendo todos los constructores – Define todos sus atributos como “private” – Proporciona métodos accesores “get” y modificadores “set” a los atributos • • • • A. Goñi. Dpto. LSI, UPV/EHU Si el atributo se llama “nomAtributo” Entonces el método get será “getNomAtributo()” Y el set: “setNomAtributo(TipoAtributo t)” Si el atributo es una propiedad booleana se usará “isNomAtributo()” en vez de “getNomAtributo()” 218 Instanciar JavaBeans • Acción JSP: <jsp:useBean atributo=valor * /> • Para encontrar (o crear si no existe) una instancia de una clase Java Bean • Posibles atributos: – id="nombre de la instancia“ – scope="page|request|session|application“ – class=“nombre de la clase Java Bean“ <jsp:useBean id="gestorBilletes" class="beans.GestorBilletesBean" scope="request" /> Crea o encuentra la instancia de la clase beans.GestorBilletesBean, accesible durante la petición actual, y la que podrá referirse con el identificador gestorBilletes A. Goñi. Dpto. LSI, UPV/EHU 219 Instanciar JavaBeans <jsp:useBean id="gestorBilletes" class="beans.GestorBilletesBean" scope="application" /> Crea o encuentra la instancia de la clase beans.GestorBilletesBean, accesible durante “toda la aplicación”, y la que podrá referirse con el identificador gestorBilletes <%! gestorBilletes = new beans.GestorBilletesBean(); } CUIDADO, NO ES EXACTAMENTE EQUIVALENTE… CON scope=“request” cada vez se crea una instancia nueva A. Goñi. Dpto. LSI, UPV/EHU 220 Llamar a JavaBeans <jsp:useBean id="gestorBilletes" class="beans.GestorBilletesBean" scope="request" /> • A partir de ese momento se puede usar el bean, directamente en Java: <% ... int res = gestorBilletes.getBillete(nombre); ... %> A. Goñi. Dpto. LSI, UPV/EHU 221 Llamar a Java Beans: setProperty • <jsp:setProperty att=val*/> – Para poner valores en atributos del Java Bean. • • • • name=“identificador del bean" property=“nombre del atributo|*" param=“nombre del parámetro de formulario" value=“valor a asignar" <jsp:setProperty name="gestorBilletes" property="nombre" value="Pepe" /> Equivalente a: <% gestorBilletes.setNombre(“Pepe”);%> <jsp:setProperty name="gestorBilletes" property="nombre" param="nom" /> Equivalente a: <% String s1 = request.getParameter("nom"); gestorBilletes.setNombre(s1);%> A. Goñi. Dpto. LSI, UPV/EHU 222 Llamar a Java Beans: getProperty <jsp:getProperty name=“identificador del bean“ property=“nombre del atributo"/> – Para obtener valores de atributos del bean <jsp:getProperty name="gestorBilletes" property="nombre" /> Equivalente a: <%= gestorBilletes.getNombre();%> NOTA: jsp:setProperty y jsp:getProperty posibilitan llamar a la lógica del negocio sin usar java: sólo lenguaje de etiquetas A. Goñi. Dpto. LSI, UPV/EHU 223 <%@ page contentType="text/html;charset=windows-1252"%> <HTML> <HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252"> <TITLE> PEDIR BILLETE </TITLE> </HEAD> <BODY> <H1> PEDIR BILLETE </H1> <FORM NAME="LE" METHOD="post" ACTION="PedirBillete.jsp"> Nombre: <INPUT TYPE="text" NAME="nombre"><P> <INPUT TYPE="submit" value="Pedir Billete"> <INPUT type="reset" VALUE="Borrar Datos"> </FORM></BODY></HTML> A. Goñi. Dpto. LSI, UPV/EHU 224 <%@ page contentType="text/html;charset=windows-1252" import="java.util.*"%> <jsp:useBean id="gestorBilletes" class="beans.GestorBilletesBean" scope="request" /> <HTML><HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252 <TITLE></TITLE> </HEAD><BODY> <H2>PEDIR BILLETE</H2><P> <% String nombre = request.getParameter("nombre"); int res = gestorBilletes.getBillete(nombre); if (res>0) out.println("<H1> Asignado. </H1> <P> </H2> Referencia: "+res+"</H2>"); else if (res==-1) out.println("<H1> No hay billetes libres</H1>"); else if (res==-2) out.println("<H1> Error: Inténtelo de nuevo.</H1>"); %> </P></BODY></HTML> A. Goñi. Dpto. LSI, UPV/EHU 225 A. Goñi. Dpto. LSI, UPV/EHU 226 package beans; import java.sql.*; public class GestorBilletesBean { public GestorBilletesBean() { try{ Class.forName("org.gjt.mm.mysql.Driver"); conexion=DriverManager.getConnection ("jdbc:mysql://localhost/Billetes"); … } public int getBillete(String nom) { String pregSQL = "SELECT NUMERO FROM BILLETES"+ " WHERE ESTADO='LIBRE'"; try{ ResultSet rs = sentencia.executeQuery(pregSQL); ..}} A. Goñi. Dpto. LSI, UPV/EHU Otra posibilidad: añadir otro nivel en la propia lógica del negocio Permite separar el servidor Web del servidor de aplicaciones. Así podría ser compartido por varios servidores Web. También se puede ver como una manera de reutilizar lógica del negocio (suponiendo que ya estaba el ServidorGestorBilletesBD) A. Goñi. Dpto. LSI, UPV/EHU 227 228 Más información… • Sobre Servlets y JSPs hay mucho más – Más directivas y acciones – Librerías de etiquetas existentes – Posibilidad de crear etiquetas propias – Trabajo con Servlets (definir sesiones, usar cookies, etc.), Applets, XML,… • Bibliografía: – Core Servlets and Java Server Pages • Libro en PDF gratuito en: http://www.coreservlets.com A. Goñi. Dpto. LSI, UPV/EHU 229 7. Introducción a los Servicios Web A. Goñi. Dpto. LSI, UPV/EHU 230 Índice • • • • • • • Introducción HTTP en 5 minutos XML en 5 minutos SOAP WSDL Usar Servicios Web en JDeveloper Otros temas A. Goñi. Dpto. LSI, UPV/EHU 231 Introducción: qué es un Servicio Web • Definición dada por el W3C – A Web service is a software system designed to support interoperable machine-to-machine interaction over a network. It has an interface described in a machine-processable format (specifically WSDL). Other systems interact with the Web service in a manner prescribed by its description using SOAP-messages, typically conveyed using HTTP with an XML serialization in conjunction with other Web-related standards • Un Servicio Web es – (Parte de) Lógica de negocio • La interfaz se define en un estándar basado en XML (WSDL) – Accesible mediante protocolos de Internet • Habitualmente HTTP y SOAP (XML) A. Goñi. Dpto. LSI, UPV/EHU 232 Introducción: Ventajas de los SW NO HAY RESTRICCIÓN SOBRE LENGUAJES, PLATAFORMAS, ETC. XML (+) Favorecen la interoperabilidad (+) Paso a través de firewalls HTTP (-) Sin embargo, tipos de datos en las llamadas son más simples… CLIENTE SERVIDOR Lenguaje JAVA Interfaces definidas en JAVA Varias máquinas y S.O. CORBA RMI IIOP CLIENTE JRMP / IIOP CLIENTE TCP/IP TCP/IP SERVIDOR Varios lenguajes: Java, C++… SERVIDOR Interfaces definidas en IDL Interfaces definidas en .NET Máquinas Windows (?) A. Goñi. Dpto. LSI, UPV/EHU Varias máquinas y S.O. . NET Remoting Varios lenguajes .NET: C++, C#, VB.NET … CLIENTE (Sustituye a DCOM) SERVIDOR 233 HTTP en 5 minutos • HTTP es un protocolo Cliente/Servidor de Internet (TCP/IP) del nivel de aplicación, que proporciona servicios de transmisión de datos entre aplicaciones. Petición SERVIDOR HTTP CLIENTE HTTP Respuesta Ejemplos de comandos en peticiones: GET Para leer una página Web POST: Para enviar datos a una página Web Ejemplos de respuestas: Estados Datos Códigos de error A. Goñi. Dpto. LSI, UPV/EHU 234 HTTP en 5 minutos A. Goñi. Dpto. LSI, UPV/EHU 235 HTTP en 5 minutos [85] sisf00 > telnet sipx55.si.ehu.es 8080 Trying 158.227.112.155... Connected to sipx55.si.ehu.es. Escape character is '^]'. GET /iso/jsp/public_html/pagina.jsp HTTP/1.0 Con telnet se puede abrir un socket interactivo con un servidor HTTP Con GET se solicita una página web (en este caso es un JSP) // Añadir línea en blanco HTTP/1.0 200 OK Content-Type: text/html;charset=windows-1252 Set-Cookie2: JSESSIONID=pjqb72fy21;Version=1;Discard;Path="/iso" Set-Cookie: JSESSIONID=pjqb72fy21;Path=/iso Servlet-Engine: Tomcat Web Server/3.2.3 (JSP 1.1; Servlet 2.2; Java 1.3.1_02; Windows 2000 5.0 x86; java.vendor=Sun Microsystems Inc.) El servidor HTTP envía datos (protocolo, código respuesta, <html> información sobre el servidor,…) <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> <title>Prueba</title> </head> <body> <h2> Hola null<br> Y el contenido de la Estamos a: Tue May 03 13:28:53 CEST 2005 página HTML </h2> </body> </html> Connection closed by foreign host. [65] sisf00 > A. Goñi. Dpto. LSI, UPV/EHU 236 HTTP en 5 minutos A. Goñi. Dpto. LSI, UPV/EHU 237 [107] sisf00 > telnet sipx55.si.ehu.es 8080 Trying 158.227.112.155... Connected to sipx55.si.ehu.es. Escape character is '^]'. POST /iso/jsp/public_html/pagina.jsp HTTP/1.0 Content-type: application/x-www-form-urlencoded Content-length: 14 HTTP en 5 minutosCon el comando POST, se pueden enviar datos al servidor HTTP. En este caso es el contenido de un parámetro, pero en general, puede ser cualquier fichero. Nombre=Alfredo HTTP/1.0 200 OK Content-Type: text/html;charset=windows-1252 Set-Cookie2: JSESSIONID=crb244gx61;Version=1;Discard;Path="/iso" Set-Cookie: JSESSIONID=crb244gx61;Path=/iso Servlet-Engine: Tomcat Web Server/3.2.3 (JSP 1.1; Servlet 2.2; Java 1.3.1_02; Wi ndows 2000 5.0 x86; java.vendor=Sun Microsystems Inc.) <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> <title>Prueba</title> </head> <body> <h2> Hola Alfredo<br> Estamos a: Tue May 03 14:11:01 CEST 2005 </h2> </body> </html> Connection closed by foreign host. [108] sisf00 > A. Goñi. Dpto. LSI, UPV/EHU 238 XML en 5 minutos • XML (eXtensible Markup Language) es un lenguaje de marcas o etiquetas que sirve para describir datos – Definido por el W3C en 1996, a partir de otro lenguaje: SGML, del cual deriva también HTML – HTML es otro lenguaje de marcas diseñado para mostrar datos • XML para almacenar, comunicar, no mostrar datos – Es extensible: se pueden definir nuevas marcas o etiquetas (tags) A. Goñi. Dpto. LSI, UPV/EHU 239 XML en 5 minutos ENCABEZAMIENTO XML: VERSIÓN Y CONJUNTO DE CARACTERES ETIQUETAS: se deben abrir y cerrar <nombreSala> …</nombreSala> OBLIGATORIO UN ÚNICO ELEMENTO RAÍZ: <entradasTodasSalas> ATRIBUTOS (siempre entre comillas, simples o dobles): <sala imagen=“principe1.gif”> ANIDACIÓN DE MARCAS: toda marca que se cierra debe corresponder a la última marca abierta y no cerrada En las etiquetas: MAYÚSCULAS ? minúsculas ETIQUETAS SIN DATOS ASOCIADOS: <nombreSala/> Equivalente a: <nombreSala></nombreSala> A. Goñi. Dpto. LSI, UPV/EHU 240 XML en 5 minutos En un navegador se puede ver el fichero XML; abrir y cerrar “etiquetas” A. Goñi. Dpto. LSI, UPV/EHU Documentos XML bien formados vs. Documentos XML válidos 241 • Un documento XML está bien formado si cumple las restricciones anteriores – No hay nodo raíz, atributos no entre comillas, mal anidamiento, etc… • Pero es posible que no sea un documento XML válido – Nombres de etiquetas, atributos erróneos – No aparición de alguna etiqueta obligatoria –… • Se puede definir la estructura de un documento XML usando DTD o XML-Schema • Qué etiquetas deben/pueden venir, qué atributos, en qué orden, etc. A. Goñi. Dpto. LSI, UPV/EHU Llamadas a servicios remotos usando XML y HTTP Se puede definir una estructura de documento XML que describa una llamada a un procedimiento remoto XML HTTP CLIENTE Se usa HTTP para la transmisión de datos XML entre las aplicaciones CLIENTE y SERVIDOR SERVIDOR POST /gestorBilletes HTTP/1.0 Content-type: text/xml Content-length: XX Por ejemplo, para llamar al método getBillete(nombre), podríamos hacerlo así: A. Goñi. Dpto. LSI, UPV/EHU <?xml version='1.0' encoding='windows-1252'?> <llamadaRemota> <getBillete> <nombre>Pepe</nombre> </getBillete> </llamadaRemota> 242 Se necesita un estándar para enviar el mensaje con la llamada remota Se puede definir una estructura de documento XML que describa una llamada a un procedimiento remoto XML HTTP CLIENTE Se usa HTTP para la transmisión de datos XML entre las aplicaciones CLIENTE y SERVIDOR SERVIDOR SIN EMBARGO: el documento XML que describe llamadas a procedimientos remotos debe seguir un estándar si no se quiere construir un servidor distinto cada vez, para que extraiga los métodos y parámetros A. Goñi. Dpto. LSI, UPV/EHU SOAP: Simple Object Access Protocol 243 SOAP: Simple Object Access Protocol 244 • SOAP 1.2 es recomendación W3C desde 24/3/2004 (http://www.w3.org/TR/soap/) • Define: – Formato de mensajes de comunicación en XML – Cómo debería ser transportado un mensaje vía Web (HTTP) o e-mail (SMTP) – Reglas que se siguen cuando se procesa un mensaje SOAP – Cómo se convierte una llamada RPC de un cliente en un mensaje SOAP, cómo se envía al servidor, cómo se convierte en una llamada RPC en el servidor, cómo se convierte la respuesta en un mensaje SOAP y se devuelve al cliente A. Goñi. Dpto. LSI, UPV/EHU 245 Formato de un mensaje SOAP SOAP Envelope SOAP header Bloque Header El mensaje se mete en un sobre (envelope) El mensaje se compone de una cabecera (header) OPCIONAL y de un cuerpo (Body) OBLIGATORIO Bloque Header SOAP Body Bloque Body La cabecera (header) se utiliza para enviar información sobre identificadores de transacciones, certificados de seguridad, información sobre coordinación, etc. Bloque Body El cuerpo (body) contiene las llamadas a los procedimientos remotos (junto con los parámetros), o bien las respuestas de dichos procedimientos remotos, o bien información sobre el error que se haya producido. A. Goñi. Dpto. LSI, UPV/EHU 246 Ejemplo de llamada SOAP getNumBillete(“Pepe”) A. Goñi. Dpto. LSI, UPV/EHU nom 247 Ejemplo de resultado en SOAP Resultado de la llamada a getNumBillete(“Pepe”) ==> 2 (como int) A. Goñi. Dpto. LSI, UPV/EHU Se necesita un estándar que describa el Servicio Web • ¿Cómo saber qué etiquetas XML podemos poner en el mensaje SOAP? <getNumBillete…> <nom …> • Es necesario conocer la definición del servicio Web (esto es, su interfaz) • Los SW se describen en WSDL WSDL: Web Services Description Language A. Goñi. Dpto. LSI, UPV/EHU 248 WSDL: Web Services Description Language 249 • WSDL 1.1 es una Nota W3C desde 15/3/2001 (http://www.w3.org/TR/wsdl/) • Define: – Una descripción abstracta de un servicio Web • El sistema de tipos usados para describir los mensajes (basado en XML Schema) • Mensajes implicados en invocar una operación • Operaciones individuales compuestas de distintos patrones de intercambio de mensajes • Una interfaz que agrupa las operaciones que forman el servicio – Una descripción concreta del servicio Web • El enlace (binding) de la interfaz a un protocolo de transporte • Dirección o punto de acceso (endpoint) del enlace (binding) • Descripción de un servicio como una colección de todos los enlaces (bindings) de la misma interfaz A. Goñi. Dpto. LSI, UPV/EHU WSDL: Web Services Description Language Documento WSDL TIPOS DE DATOS USADOS (los de XML Schema) DESCRIPCIÓN ABSTRACTA DEL SERVICIO Types Message (REQUEST) Message (RESPONSE) Message (REQUEST) Message (RESPONSE) Operation 2 Operation 1 DESCRIPCIÓN CONCRETA DEL SERVICIO MENSAJES DE PETICIÓN Y RESPUESTA PARA CADA OPERACIÓN SERVICIO ABSTRACTO (CONJUNTO DE OPERACIONES) Interface A. Goñi. Dpto. LSI, UPV/EHU 250 binding 1 binding 2 binding 3 ENLACES A PROTOCOLOS DE TRANSPORTE endpoint 1 endpoint 2 endpoint 3 DIRECCIONES DE LOS ENLACES Service TODAS LAS IMPLEMENTACIONES DEL SERVICIO 251 WSDL Types Message (REQUEST) Message (RESPONSE) Message (REQUEST) Message (RESPONSE) Operation 2 Operation 1 Interface A. Goñi. Dpto. LSI, UPV/EHU binding 1 binding 2 binding 3 endpoint 1 endpoint 2 endpoint 3 Service WSDL: Web Services Description Language – Una descripción abstracta de un servicio Web • El sistema de tipos usados para describir los mensajes (basado en XML Schema) • • • – Mensajes implicados en invocar una operación Operaciones individuales compuestas de distintos patrones de intercambio de mensajes Una interfaz que agrupa las operaciones que forman el servicio Una descripción concreta del servicio Web • • • El enlace (binding) de la interfaz a un protocolo de transporte Dirección o punto de acceso (endpoint) del enlace (binding) Descripción de un servicio como una colección de todos los enlaces (bindings) de la misma interfaz A. Goñi. Dpto. LSI, UPV/EHU types 252 WSDL: Web Services Description Language – Una descripción abstracta de un servicio Web • El sistema de tipos usados para describir los mensajes (basado en XML Schema) • Mensajes implicados en invocar una operación • • – message Operaciones individuales compuestas de distintos patrones de intercambio de mensajes Una interfaz que agrupa las operaciones que forman el servicio Una descripción concreta del servicio Web • • • El enlace (binding) de la interfaz a un protocolo de transporte Dirección o punto de acceso (endpoint) del enlace (binding) Descripción de un servicio como una colección de todos los enlaces (bindings) de la misma interfaz A. Goñi. Dpto. LSI, UPV/EHU 253 WSDL: Web Services Description Language – Una descripción abstracta de un servicio Web • El sistema de tipos usados para describir los mensajes (basado en XML Schema) • Mensajes implicados en invocar una operación • Operaciones individuales compuestas de distintos operation patrones de intercambio de mensajes • Una interfaz que agrupa las operaciones que forman el portType servicio – Una descripción concreta del servicio Web • • • El enlace (binding) de la interfaz a un protocolo de transporte Dirección o punto de acceso (endpoint) del enlace (binding) Descripción de un servicio como una colección de todos los enlaces (bindings) de la misma interfaz A. Goñi. Dpto. LSI, UPV/EHU 254 WSDL: Web Services Description Language – Una descripción abstracta de un servicio Web • – El sistema de tipos usados para describir los mensajes (basado en XML Schema) • Mensajes implicados en invocar una operación • Operaciones individuales compuestas de distintos patrones de intercambio de mensajes • Una interfaz que agrupa las operaciones que forman el servicio Una descripción concreta del servicio Web • El enlace (binding) de la interfaz a un protocolo de transporte • • A. Goñi. Dpto. LSI, UPV/EHU binding Dirección o punto de acceso (endpoint) del enlace (binding ) Descripción de un servicio como una colección de todos los enlaces (bindings) de la misma interfaz 255 256 WSDL: Web Services Description Language – Una descripción abstracta de un servicio Web • – El sistema de tipos usados para describir los mensajes (basado en XML Schema) • Mensajes implicados en invocar una operación • Operaciones individuales compuestas de distintos patrones de intercambio de mensajes • Una interfaz que agrupa las operaciones que forman el servicio Una descripción concreta del servicio Web • El enlace (binding) de la interfaz a un protocolo de transporte • Dirección o punto de acceso (endpoint) del enlace (binding) • Descripción de un servicio como una colección de todos los enlaces (bindings) de la misma interfaz A. Goñi. Dpto. LSI, UPV/EHU port service 257 Punto de acceso (endpoint) al SW A. Goñi. Dpto. LSI, UPV/EHU 258 A. Goñi. Dpto. LSI, UPV/EHU 259 RESULTADO Mensaje SOAP con la respuesta A. Goñi. Dpto. LSI, UPV/EHU 260 Usar SW en JDeveloper • Afortunadamente, los distintos entornos ofrecen herramientas y asistentes que generan los WSDL y SOAP de manera automática, a partir de clases implementadas en distintos Leng. Prog. • Los programadores pueden seguir trabajando con sus lenguajes y plataformas habituales • JDeveloper, en concreto, lo permite A. Goñi. Dpto. LSI, UPV/EHU 261 Crear un SW en Java A. Goñi. Dpto. LSI, UPV/EHU Se selecciona la clase Java para la que se quiere crear el SW A. Goñi. Dpto. LSI, UPV/EHU 262 Seleccionar los métodos que se quieren publicar en el SW ¡Cuidado! No se pueden crear SW para métodos cuyos tipos no sean los básicos, String, Date, Calendar,… o Array[] de ellos A. Goñi. Dpto. LSI, UPV/EHU 263 Se indica la dirección del punto de acceso A. Goñi. Dpto. LSI, UPV/EHU 264 El WSDL se genera automáticamente A. Goñi. Dpto. LSI, UPV/EHU 265 266 Punto acceso A. Goñi. Dpto. LSI, UPV/EHU Creamos una clase Java cliente del SW A. Goñi. Dpto. LSI, UPV/EHU 267 268 A. Goñi. Dpto. LSI, UPV/EHU 269 Añadimos el código con la llamada al SW (en Java) A. Goñi. Dpto. LSI, UPV/EHU 270 Ejecutamos el SW A. Goñi. Dpto. LSI, UPV/EHU 271 Ejecutamos el cliente del SW A. Goñi. Dpto. LSI, UPV/EHU 272 Y activamos el Monitor TCP para ver los mensajes SOAP Tools => TCP Packet Monitor A. Goñi. Dpto. LSI, UPV/EHU 273 Mensaje SOAP con la petición A. Goñi. Dpto. LSI, UPV/EHU Mensaje SOAP con la respuesta 274 Otros temas • UDDI (Universal Description, Discovery and Integration) – Es un directorio distribuido donde las empresas pueden registrar, eliminar y buscar servicios web. – Empresas como IBM, Microsoft, etc. mantienen nodos con esa información. • Se pretende construir una infraestructura para construir aplicaciones integrando servicios web entre empresas (B2B) • Herramientas para traducir de WSDL a un lenguaje (Java, lenguaje .NET, …) – Apache AXIS proporciona compiladores Java a WSDL y de WSDL a Java • Definición de otros estándares como WS-Coordination, WSTransaction, WS-Routing – ¿Qué pasa si se quiere reservar un viaje completo? • Reservar vuelo => Usando SW • Reservar hotel => Usando SW • ¿Y si quisiéramos que fuera una transacción? A. Goñi. Dpto. LSI, UPV/EHU 275 Ejercicio de Arquitecturas Software A. Goñi. Dpto. LSI, UPV/EHU 276 La interfaz de usuario asociada a un caso de uso llamado CONSULTAR PRECIO aparece a continuación, junto con la clase Java correspondiente: A. Goñi. Dpto. LSI, UPV/EHU import java.awt.*; import java.awt.event.*; public class ConsPrecioIU extends Frame { Label label1 = new Label(); Panel panel1 = new Panel(); Button button1 = new Button(); Button button2 = new Button(); Panel panel2 = new Panel(); GridLayout gridLayout1 = new GridLayout(3,2); Label label2 = new Label(); TextField textField1 = new TextField(); Label label3 = new Label(); TextField textField2 = new TextField(); Label label4 = new Label(); TextField textField3 = new TextField(); public ConsPrecioIU() { super(); try { jbInit(); } catch (Exception e) { e.printStackTrace(); } } // Continúa… private void jbInit() throws Exception { this.setTitle("Frame Title"); label1.setText("CONSULTAR PRECIO"); label1.setAlignment(Label.CENTER); button1.setLabel("Consultar Precio"); button1.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { button1_actionPerformed(e); } }); button2.setLabel("Cancelar"); button2.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { button2_actionPerformed(e); } }); label2.setText("MANZANAS (Kg.)"); label3.setText("PERAS (Kg.)"); label4.setText("NARANJAS (Kg.)"); panel2.setLayout(gridLayout1); this.add(label1, BorderLayout.NORTH); this.add(panel1, BorderLayout.SOUTH); panel1.add(button1, null); panel1.add(button2, null); this.add(panel2, BorderLayout.CENTER); panel2.add(label2, null); panel2.add(textField1, null); panel2.add(label3, null); panel2.add(textField2, null); panel2.add(label4, null); panel2.add(textField3, null); this.pack(); this.setVisible(true);} void button1_actionPerformed(ActionEvent e) {...} void button2_actionPerformed(ActionEvent e) {...} A. Goñi. Dpto. } LSI, UPV/EHU 277 278 Se dispone también de una clase llamada Aviso que sirve para crear Dialog modales asociados al objeto Frame actual. La llamada new Aviso(this,"Pulsa Aceptar y me voy"); crearía lo siguiente: Además, nos han proporcionado los siguientes métodos, los cuales no sabemos ni a qué clase pertenecen ni qué es lo que hacen exactamente, pero nos han dicho que son útiles para acceder a los datos almacenados en la siguiente tabla de una BD Access. Además nos dicen que dicha BD es accesible por medio de una fuente de datos ODBC llamada PRODS A. Goñi. Dpto. LSI, UPV/EHU 279 public void inicializarBD () { try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conexion=DriverManager.getConnection("jdbc:odbc:PRODS"); sentencia=conexion.createStatement(); } catch(Exception e){System.out.println("Error"+e.toString());} } public float getPrecio(String nombre) { try{ rs=sentencia.executeQuery("SELECT PRECIO FROM PRODUCTOS "+ "WHERE NOMBRE='"+nombre+"'"); if (rs.next()) return rs.getFloat("PRECIO"); } catch (Exception e) {System.out.println("Error: "+e.toString());} return 0; } A. Goñi. Dpto. LSI, UPV/EHU Se pide: Rellenar la clase ConsPrecioIU con el código necesario para que al pulsar el botón CONSULTAR PRECIO aparezca como resultado el precio de los productos escogidos. Por ejemplo, el resultado sería el siguiente: si los precios actuales fueran los que aparecen en la tabla ACCESS anterior y se hubiera pulsado el botón CONSULTAR PRECIO con los siguientes datos de entrada: La solución debe basarse en una arquitectura lógica en 3 niveles y ser extensible ante un futuro cambio en la lógica del negocio, ya que se está pensando en “aplicar porcentajes de descuento a cada producto dependiendo de la cantidad de Kg. que se compre”. A. Goñi. Dpto. LSI, UPV/EHU 280 281 Solución ConsPrecioIU p: PreciosLN “interface” PreciosLN usa calcularPrecio(c1,c2,c3: String): float Precios inicializarBD(): void getPrecio(nombre:String): float calcularPrecio(kgMan,kgPer,kgNar: String) : float A. Goñi. Dpto. LSI, UPV/EHU EN LA PRESENTACIÓN AÑADIMOS UN ATRIBUTO CON LA LÓGICA DEL NEGOCIO Y MÉTODO PARA ASIGNARLA: 282 public class ConsPrecioIU extends Frame { … PreciosLN pr; void setLogicaNegocio(PreciosLN p) {pr=n;} …} LA LÓGICA DEL NEGOCIO SE DEFINE CON UNA INTERFAZ: public interface PreciosLN { public float calcularPrecio(String kgManz, String kgPer, String kgNar); } A. Goñi. Dpto. LSI, UPV/EHU 283 LA LÓGICA DEL NEGOCIO SE USA DESDE EL MÉTODO DE ATENCIÓN AL EVENTO void button1_actionPerformed(ActionEvent e) { // En p tenemos el objeto con la lógica del negocio float precio = p.calcularPrecio(textField1.getText(), textField2.getText(), textField3.getText()); Aviso a = new Aviso(this,"Precio es: "+precio); } A. Goñi. Dpto. LSI, UPV/EHU 284 public class Precios implements PreciosLN { public float calcularPrecio(String kgManz, String kgPer, String kgNar) { float m,p,n; try{m=Float.parseFloat(kgManz);} catch (Exception ex) {m=0;} try{p=Float.parseFloat(kgPer);} catch (Exception ex) {p=0;} try{n=Float.parseFloat(kgNar);} catch (Exception ex) {n=0;} return m*getPrecio("MANZANAS (Kg.)")+ p*getPrecio("PERAS (Kg.)")+ n*getPrecio("NARANJAS (Kg.)");} // y los métodos inicializarBD y getPrecio… } LA LÓGICA DEL NEGOCIO SE OBTIENE IMPLEMENTANDO LA INTERFAZ. PARA ELLO SE REALIZAN LLAMADAS AL NIVEL DE DATOS A. Goñi. Dpto. LSI, UPV/EHU 285 Es una solución correcta… • La solución sigue una arquitectura lógica en tres niveles (están separadas el nivel de presentación del nivel lógica del negocio en clases y el nivel de datos en la BD) – Presentación: ConsPrecioIU y Aviso – Lógica del negocio: interfaz PreciosLN y clase Precios • Es extensible – Cuando se quiera añadir la regla del negocio para aplicar descuentos según la cantidad comprada habrá que reprogramar la clase calcularPrecios de la lógica del negocio y la BD (para almacenar descuentos, etc.). Pero NO CAMBIARÁ la clase de presentación A. Goñi. Dpto. LSI, UPV/EHU La siguiente solución no es correcta… “interface” PreciosLN ConsPrecioIU p: PreciosLN usa getPrecioMan(): float getPrecioPer(): float, getPrecioNar(): float Precios inicializarBD(): void getPrecio(nombre:String): float getPrecioMan(): float getPrecioPer(): float, getPrecioNar(): float A. Goñi. Dpto. LSI, UPV/EHU 286 287 LA LÓGICA DEL NEGOCIO SE USA DESDE EL MÉTODO DE ATENCIÓN AL EVENTO void button1_actionPerformed(ActionEvent e) { // En pr tenemos el objeto con la lógica del negocio String kgManz = textField1.getText(); // idem. kgPer y KgNar try{m=Float.parseFloat(kgManz);} catch (Exception ex) {m=0;} try{p=Float.parseFloat(kgPer);} catch (Exception ex) {p=0;} try{n=Float.parseFloat(kgNar);} catch (Exception ex) {n=0;} float precio = m*pr.getPrecioMan()+ p*pr.getPrecioPer() + n*pr.getPrecioNar(); Aviso a = new Aviso(this,"Precio es: "+precio);} NO ES EXTENSIBLE. EL PRECIO DE LOS PRODUCTOS NO DEPENDE DE LA CANTIDAD. HABRÍA QUE CAMBIAR LA PRESENTACIÓN: if (m>10) precioMan = pr.getPrecioMan()*0.9; // idem. con resto de productos y descuentos A. Goñi. Dpto. LSI, UPV/EHU 288 La solución propuesta, sin embargo, no es extensible si se desea cambiar el número y el nombre de productos (lo cual no se pedía en el enunciado…) A. Goñi. Dpto. LSI, UPV/EHU 289 • Una solución extensible ante el posible cambio “se pueden consultar los precios de varios productos” aparece a continuación. • Para ver si una solución es extensible hay que saber con respecto a qué posible cambio es extensible A. Goñi. Dpto. LSI, UPV/EHU void button1_actionPerformed(ActionEvent e) { // En p tenemos el objeto con la lógica del negocio Vector datos= new Vector(); datos.addElement(label1.getText()); datos.addElement(textField1.getText()); datos.addElement(label2.getText()); // … float precio = p.calcularPrecio(datos.elements()); Aviso a = new Aviso(this,"Precio es: "+precio); } “interface” PreciosLN ConsPrecioIU p: PreciosLN usa calcularPrecio(datos: Enumeration): float obtenerProductos(): Enumeration Precios inicializarBD(): void getPrecio(nombre:String): float calcularPrecio(PrecCant:Enumeration ) : float obtenerProductos(): Enumeration A. Goñi. Dpto. LSI, UPV/EHU 290