INGENIERÍA DEL SOFTWARE. 4º ING. INFORMÁTICA (UPV/EHU) 29 de MAYO de 2002 NOMBRE: GRUPO: 1.- (0,5 ptos.) (Tiempo estimado: 10’) Enuncia cuáles son las interfaces que se tienen que definir para desarrollar un EJB, así como cuál es su finalidad. La interfaz home: Tiene la signatura de los métodos para crear/destruir objetos EJB. La interfaz remota: La interfaz con la que opera el cliente cuando quiere invocar al EJB. La interfaz local: tiene la misma funcionalidad que la interfaz remota. Sin embargo esta interfaz será utilizada únicamente cuando el cliente y el objeto que implementa esta interfaz estén en la misma má quina. La interfaz local home: tiene la misma funcionalidad que la interfaz home. Se utilizará en el mismo contexto que la interfaz anterior. 2.- (0,5 ptos.) (Tiempo estimado: 10’) Explica qué son los RESGUARDOS y los CONTROLADORES, y su relación con los COMPONENTES y PROCEDIMIENTOS DE PRUEBA RESGUARDO: módulo que sustituye a otro módulo real llamado desde el módulo que se está probando (pruebas con integración descendente). CONTROLADOR: módulo que llama a un módulo real a probar con los datos de entrada especificados en distintos casos de prueba (pruebas con INTEGRACIÓN ASCENDENTE). Los COMPONENTES y PROCEDIMIENTOS DE PRUEBA son artefactos utilizados en el Proceso Unificado. Un procedimiento de prueba especifica cómo realizar uno o varios casos de prueba y los componentes de prueba automatizan procedimientos de prueba, si es posible. Podríamos decir que el concepto de controlador y el de componente de prueba es el mismo. 3.- (2,5 ptos.) (Tiempo estimado: 40’) Queremos trabajar con una especie de base de datos utilizando JGL. Para ello, se utilizará un Container JGL para almacenar las “tuplas” y el siguiente algoritmo JGL para realizar consultas de selección: Filtering.select(Container, UnaryPredicate) El predicado JGL UnaryPredicate se utiliza para expresar la condición de la selección. Se pueden realizar preguntas con condiciones compuestas con el siguiente UnaryPredicate: UnaryAnd(UnaryPredicate, UnaryPredicate) En concreto, se puede conseguir la conjunción lógica de dos predicados, esto es, que se devuelva true si y sólo si ambos predicados devuelven true. Para visualizar los resultados podemos utilizar este otro algoritmo JGL , que aplica una función a todos los elementos del Container. Applying.forEach(Container, UnaryFunction) A continuación se presenta el diseño de tres clases: Empleado, Becario y Profesor y el código INCOMPLETO de una clase Principal que visualiza la respuesta a la siguiente consulta: CONSULTA: encontrar todos los profesores que ganan más de 150.000 y que son de Lezo Empleado -nombre : String -diasTrabajados : int -sueldoDiario- : double -ciudad : String +getSueldo() : double +toString() : String Becario Profesor -proyecto : String -departamento : String +toString() : String +toString() : String // Omitimos los imports de JGL public class Principal { public static void main (String[] args) { double topeSueldo = 150000.0; String ciudad = "Lezo"; Container datos = new Array(); datos.add(new datos.add(new datos.add(new datos.add(new Becario("Leizarraga",20.0, 1300.0, "Aduna","UPV-EHU") ); Becario("Lizarralde",25.0,1200.0, "Lekeitio","DIPU")); Profesor("Fernandez",120.0, 3000.0, "Lezo","LSI") ); Profesor("Zelaia",50.0, 4000.0, "Lezo","ATC") ); UnaryPredicate profesor UnaryPredicate sueldo UnaryPredicate ciudadano = new InstanciaDeProfesor(); = new TopesSueldo(topeSueldo); = new CiudadanoDe(ciudad); Container resultado = Filtering.select(?????, ??????); // Usar Applying.forEach(??????, ??????) para visualizar datos } } fin de la clase Principal Se pide: realizar la implementación de las clases necesarias y completar la clase Principal. SOLUCIÓN: UnaryPredicate ciudadano = new BindSecondPredicate(new EqualString(),”Lezo”); UnaryPredicate sueldo = new BindSecondPredicate(new GreaterNumber(),150000); UnaryPredicate profesor= new EsProfesor(); UnaryPredicate up1=new UnaryAnd(profesor, sueldo); UnaryPredicate up2=new UnaryAnd(up1,ciudadano); UnaryFunction ver=new VerDatos(); Container resultado = Filtering.select(datos, up2); Applying.forEach(resultado, ver); class EsProfesor implements UnaryPredicate { public boolean execute(o) { return o.instanceOf(Profesor); } } class VerDatos implements UnaryFunction { public void execute(o) { Empleado emp=(Empleado)o; System.out.println(emp.nombre+” “+emp .ciudad+” “+emp.sueldo); } } 4.- (3 ptos.) (Tiempo estimado: 45’) El diagrama de secuencia con el diseño del caso de uso “Buscar Personas Por Nombre” Buscar Personas Por Nombre Usuario es el siguiente: : IU_VP GBD: Statement : GestorPersonas usuario escribir Nombre() buscarPorNombre(nombre:String) executeQuery("select * from persona where nombre= %nombre") next() new() r: ResultSet getString("nombre"),...,: getString("telefono") new Persona(nombre:String, direccion:String, telefono:String) Pi: Persona P1, P2, ... Pn() getNombre() getDireccion() getTelefono() Esta es la interfaz gráfica de usuario correspondiente al caso de uso: A continuación presentamos una implementación INCOMPLETA en Java de dicho caso de uso utilizando una arquitectura física en tres niveles. package jun02; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.rmi.*; public class IU_VP extends JFrame { JLabel jLabel1 = new JLabel(); JTextField jTextField1 = new JTextField(); JTextArea jTextArea1 = new JTextArea(); GestorPersonas g; public IU_VP() { try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } private void jbInit() throws Exception { jLabel1.setBounds(new Rectangle(17, 41, 130, 30)); this.getContentPane().setLayout(null); this.setSize(new Dimension(400, 300)); this.setTitle("Pertsonen bilakatzaile / Buscador de Personas"); jLabel1.setRequestFocusEnabled(false); jLabel1.setText("Izena / Nombre"); jTextField1.setBounds(new Rectangle(122, 40, 197, 33)); jTextField1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { jTextField1_actionPerformed(e); } }); jTextArea1.setBounds(new Rectangle(45, 97, 261, 145)); this.getContentPane().add(jTextArea1, null); this.getContentPane().add(jTextField1, null); this.getContentPane().add(jLabel1, null); } public void setLogicaNegocio(GestorPersonas gp) {g=gp;} void jTextField1_actionPerformed(ActionEvent e) { } public static void main(String[] argc) { IU_VP i = new IU_VP(); i.setVisible(true); try{ System.setSecurityManager(new RMISecurityManager()); String url = i.setLogicaNegocio((GestorPersonas)Naming.lookup(url)); } catch (Exception ex) { } } } package jun02; import com.objectspace.jgl.Container; public interface GestorPersonas { /** Método que busca personas con un determinado nombre * @param n Nombre de la persona a buscar * @return Container con los objetos de Persona que tienen dicho nombre */ Container buscarPorNombre(String n) } package jun02; public class Persona { private String nombre; private String direccion; private String telefono; public Persona(String n, String dir, String tel) { nombre=n; direccion=dir; telefono=tel; } public String getNombre(){return nombre;} public String getDireccion() {return direccion;} public String getTelefono() {return telefono;} } package jun02; import java.rmi.server.*; import java.rmi.*; import java.sql.*; import java.util.*; import com.objectspace.jgl.Container; import com.objectspace.jgl.Array; public class ImplGestorPersonas { PreparedStatement s; Connection o; public ImplGestorPersonas() throws RemoteException { try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); o=DriverManager.getConnection("jdbc:odbc:BDPers"); s=o.prepareStatement("select nombre, direccion, telefono"+ " from persona where nombre like ?"); } catch (Exception ex){System.out.println("Error: "+ex.toString());} } public Container buscarPorNombre(String n) throws RemoteException { Array v=new Array(); } public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try { ImplGestorPersonas objetoServidor = new ImplGestorPersonas(); try { java.rmi.registry.LocateRegistry.createRegistry(1200); } catch (Exception e) {System.out.println("Rmiregistry ya lanzado");} Naming.rebind("//localhost:1200/gestorPersonas", ); System.out.println("Lanzado el objeto servidor"); } catch (Exception e) {System.out.println("Error al lanzar el servidor"+e.toString());} }} Se pide implementar el código correspondiente a la respuesta al evento “escribir un nombre en la caja de texto de la interfaz gráfica de usuario”, la implementación del método buscarPor Nombre y las llamadas a los métodos lookup y rebind de RMI. Además existen algunos errores claros que hay que corregir. SOLUCIÓN: package jun02; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.rmi.*; public class IU_VP extends JFrame { JLabel jLabel1 = new JLabel(); JTextField jTextField1 = new JTextField(); JTextArea jTextArea1 = new JTextArea(); GestorPersonas g; public IU_VP() { try { jbInit(); } catch(Exception e) { e.printStackTrace(); } } private void jbInit() throws Exception { jLabel1.setBounds(new Rectangle(17, 41, 130, 30)); this.getContentPane().setLayout(null); this.setSize(new Dimension(400, 300)); this.setTitle("Pertsonen bilakatzaile / Buscador de Personas"); jLabel1.setRequestFocusEnabled(false); jLabel1.setText("Izena / Nombre"); jTextField1.setBounds(new Rectangle(122, 40, 197, 33)); jTextField1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { jTextField1_actionPerformed(e); } }); jTextArea1.setBounds(new Rectangle(45, 97, 261, 145)); this.getContentPane().add(jTextArea1, null); this.getContentPane().add(jTextField1, null); this.getContentPane().add(jLabel1, null); } public void setLogicaNegocio(GestorPersonas gp) {g=gp;} void jTextField1_actionPerformed(ActionEvent e) { String nombre = jTextField1.getText(); try{ Enumeration en = g.buscarPorNombre(nombre).elements(); Persona p; jTextArea1.setText(""); while (en.hasMoreElements()) { p = (Persona)en.nextElement(); jTextArea1.append(p.getNombre()+" "+p.getDireccion()+ " "+p.getTelefono()+"\n"); } } catch(Exception ex) {jTextArea1.append("Error en la conexión: "+ex.toString());} } public static void main(String[] argc) { IU_VP i = new IU_VP(); i.setVisible(true); try{ System.setSecurityManager(new RMISecurityManager()); String url = "rmi://localhost:1200/gestorPersonas"; i.setLogicaNegocio((GestorPersonas)Naming.lookup(url)); } catch (Exception ex) {i.jTextArea1.setText("Error al asignar la lógica del negocio\n"); i.jTextArea1.append(ex.toString());} } } package jun02; import com.objectspace.jgl.Container; public interface GestorPersonas extends java.rmi.Remote { /** Método que busca personas con un determinado nombre * @param n Nombre de la persona a buscar * @return Container con los objetos de Persona que tienen dicho nombre */ Container buscarPorNombre(String n) throws java.rmi.RemoteException; } package jun02; public class Persona implements java.io.Serializable { private String nombre; private String direccion; private String telefono; public Persona(String n, String dir, String tel) { nombre=n; direccion=dir; telefono=tel; } public String getNombre(){return nombre;} public String getDireccion() {return direccion;} public String getTelefono() {return telefono;} } package jun02; import java.rmi.server.*; import java.rmi.*; import java.sql.*; import java.util.*; import com.objectspace.jgl.Container; import com.objectspace.jgl.Array; public class ImplGestorPersonas extends UnicastRemoteObject implements GestorPersonas { PreparedStatement s; Connection o; public ImplGestorPersonas() throws RemoteException { try{ Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); o=DriverManager.getConnection("jdbc:odbc:BDPers"); s=o.prepareStatement("select nombre, direccion, telefono"+ " from persona where nombre like ?"); } catch (Exception ex){System.out.println("Error: "+ex.toString());} } public Container buscarPorNombre(String n) throws RemoteException { Array v=new Array(); try{ s.setString(1,"%"+n+"%"); ResultSet r=s.executeQuery(); while (r.next()) { v.add(new Persona(r.getString("nombre"), r.getString("direccion"), r.getString("telefono")));} } catch (Exception ex) {System.out.println("Error: "+ex.toString());} return v; } public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try { ImplGestorPersonas objetoServidor = new ImplGestorPersonas(); try { java.rmi.registry.LocateRegistry.createRegistry(1200); } catch (Exception e) {System.out.println("Rmiregistry ya lanzado");} Naming.rebind("//localhost:1200/gestorPersonas",objetoServidor); System.out.println("Lanzado el objeto servidor"); } catch (Exception e) {System.out.println("Error al lanzar el servidor"+e.toString());} }} 5.- (3,5 ptos.) (Tiempo estimado: 60’) Una operadora de telefonía ha puesto un nuevo servicio disponible a sus clientes. El servicio “aviso de llamadas” consiste en enviar a los dispositivos pre establecidos por el cliente (correo electrónico, buscador, móvil o teléfono fijo ) la llegada de una llamada de cualquiera de sus dispositivos. En el mensaje se indica el número del dispositivo a partir del cual se ha realizado la llamada. Los dispositivos se subscriben a otros dispositivos mediante el método subscribir(dispositivo) y se pueden dar de baja mediante el método unsubscribir(dispositivo). Suponiendo que el programa principal y su salida sea la siguiente: public static void main(String[] args) { TelefonoFijo tfijo=new TelefonoFijo("943015052"); TelefonoMovil tmovil=new TelefonoMovil("Mi movil"); CorreoElectronico correo=new CorreoElectronico("iso@si.ehu.es"); Buscador bus=new Buscador("El busca de pepe"); tfijo.subscribir(tmovil); tfijo.subscribir(bus); tfijo.llamada(new TelefonoFijo("943015555")); tfijo.unsubscribir(bus); tfijo.llamada(correo); correo.subscribir(tmovil); correo.subscribir(tfijo); correo.llamada(bus); } Mensaje Mensaje Mensaje Mensaje Mensaje al al al al al busca recibido en 943015052 enviado desde 943015555 móvil recibido en 943015052 enviado desde 943015555 móvil recibido en 943015052 enviado desde iso@si.ehu.es fijo recibido en iso@si.ehu.es enviado desde El busca de pepe móvil recibido en iso@si.ehu.es enviado desde El busca de pepe SE PIDE: 1. Diagrama de clases para modelar la aplicación, con los métodos que estimes relevantes 2. Implementación de las clases del diseño. 3. Recientemente la operadora desea construir un nuevo módulo “almacenamiento de llamadas”. Este módulo permite almacenar llamadas a través del método insertLlamada(String numero) y devolver el conjunto de llamadas almacenadas a través del método getLlamadas(). Diseña e implementa este módulo teniendo en cuenta que (1) NO debes ofrecer detalles al cliente de este módulo, acerca de la implementación y (2) NO debes utilizar ninguna librería de java en la signatura del método getLlamadas(). 4. Modifica el diseño inicial para poder añadir la funcionalidad getLlamadasRecibidas() (p.ej. correo.getLlamadasRecibidas()) a todos los dispositivos del sistema. Esta funcionalidad nos devuelve todas llamadas redirigidas a un dispositivo. 5. Implementa los métodos necesarios para dar sopo rte al servicio del punto 4. SOLUCIÓN: 1.- Observable Observer m..n (extends) (implements) Dispositivo subscribir unsubscribir getUltimo llamada (extends) TelefonoFijo update() (extends) Buscador update() (extends) TelefonoMovil update() (extends) CorreoElectronico update() 2.- El código que habría que implementar es el de las clases Dispositivo y los dispositivos concretos. En este caso únicamente se muestra la implementación de la clase TelefonoMovil public abstract class Dispositivo extends Observable implements Observer { String ultimoNumero; String identificador; public Dispositivo(String pIdent) { identificador=pIdent; } public String getIdentificador() {return identificador;} public void subscribir(Dispositivo pDisp) { addObserver(pDisp); } public void unsubscribir(Dispositivo pDisp) { deleteObserver(pDisp); } public String getUltimo(){ return ultimoNumero; } public void llamada(Dispositivo disp) { ultimoNumero=disp.getIdentificador(); setChanged(); notifyObservers(); } } public class TelefonoMovil extends Dispositivo { public TelefonoMovil(String ident) { super(ident); } public void update(Observable t, Object o){ Dispositivo disp=(Dispositivo)t; System.out.println("Mensaje al movil "+identificador+" recibido en "+disp.getIdentificador()+" enviado desde "+disp.getUltimo()); } } 3.- Agregado elements() Enumeration (use) (implements) MemoLlamada sgetLlamadas() añadirLlamada hasMoreElements() nextElement() (implements) EnumLlamds (use) hasMoreElements() nextElement() Se ha utilizado el patrón ITERATOR. Por lo tanto habrá que implementar las clases MemoLlamadas y EnumLlamds. public class MemoLlamadas { public Vector v; public MemoLlamadas() { v=new Vector(); } public void añadirLlamada(String s) { v.add(s); } public Enumeration getLlamadas() { return new MemoEnumerationLlamadas(this); } } public class MemoEnumerationLlamadas implements Enumeration { MemoLlamadas memo; int pos=0; public MemoEnumerationLlamadas(MemoLlamadas m) { memo=m; } public boolean hasMoreElements() { return (memo.v.size()>pos); } public Object nextElement() { return memo.v.elementAt(pos++); } } 4.Observable Observer Agregado m..n elements() Dispositivo subscribir() unsubscribir() getUltimo() llamada() getLlamadasRecibidas (use) elements() EnumLlamds (use) (extends) (extends) Buscador (extends) TelefonoMovil update() update() update() CorreoElectronico update() hasMoreElements() nextElement() (implements) MemoLlamadas (extends) TelefonoFijo (use) (implements) (implements) (extends) Enumeration hasMoreElements() nextElement() 5.- Los cambios que habría que realizar son los siguientes public abstract class Dispositivo extends Observable implements Observer { protected MemoLlamadas memo; public Enumeration getLlamadasRecibidas() { return memo.elements(); } public void addLlamada(String pNumero) { memo.añadirLlamada(pNumero); } public class TelefonoMovil extends Dispositivo { public void update(Observable t, Object o){ Dispositivo disp=(Dispositivo)t; System.out.println("Mensaje al movil "+identificador+" recibido en "+disp.getIdentificador()+" enviado desde "+disp.getUltimo()); addLlamada(disp.getUltimo()); } }